Sign in Join
Wiki copytemporary

macOS Audio Player: what changed

Revision 0 to 1. Removed lines have a minus sign; added lines have a plus sign.

Title: macOS Audio Player

OldNewLine
1+*(macOS only)*
2+
3+## Supported Audio File and Data Formats
4+
5+## AVAudioPlayer
6+
7+The AVAudioPlayer class lets you play sound in any audio format available in macOS from a file or memory. The properties of this class are used for managing information about a sound such as the playback point within the sound’s timeline and for accessing playback options such as volume and looping.
8+
9+Using an audio player you can:
10+
11+- Play sounds of any duration (unlike [[macOS Play Alert Sound|System Sound Services]] that has a 30 second limit);
12+- Play sounds from files or memory buffers;
13+- Play sounds when your application is in the background;
14+- Loop sounds (including indefinitely);
15+- Play multiple sounds simultaneously, one sound per audio player, with precise synchronisation;
16+- Control relative playback level, stereo positioning, and playback rate for each sound you are playing;
17+- Seek to a particular point in a sound file, which supports such application features as fast forward and rewind; and
18+- Obtain data you can use for playback-level metering.
19+
20+## Example Code
21+
22+The example code below provides a demo audio player application which implements the basic requirements for such an application.
23+
24+> **Note:** Not every method in the AVAudioPlayer class has been implemented in the code below. Most methods have been implemented: play audio from a file, pause playback, resume playback, stop playback, set/get volume, set/get number of loops, set/get current time (ie forward, rewind) and set/get rate (ie speed) of playback; etc. Check the Apple documentation for the rest. The implemented methods are described in detail in the code below.
25+
26+``` pascal
27+//
28+// Note: Lazarus IDE 2.2.0 and Free Pascal Compiler v3.2.2 used
29+//
30+
31+unit unit1;
32+
33+{$mode objfpc}{$H+}
34+{$modeswitch objectivec1}
35+{$linkframework AVFoundation}
36+
37+interface
38+
39+uses
40+ Forms, // for Form
41+ SysUtils, // for FloatToStr
42+ StdCtrls, // for labels, buttons
43+ CocoaAll, // for Cocoa types
44+ ComCtrls, // for trackbar
45+ ExtCtrls; // for timer
46+
47+type
48+
49+ { TForm1 }
50+ AVAudioPlayerPtr = ^AVAudioPlayer;
51+ AVAudioPlayerDelegateProtocolPtr = ^AVAudioPlayerDelegateProtocol;
52+
53+ AVAudioPlayerDelegateProtocol = ObjCProtocol;
54+
55+ AVAudioPlayer = objcclass external(NSObject)
56+ private
57+ _internal: id;
58+ public
59+ {Initializes and returns an audio player for playing a designated sound file.
60+ url: a URL identifying the sound file to play. The audio data must be in a
61+ format supported by Core Audio.
62+ outError: If an error occurs, on return the NSError object describes the
63+ error. Set to NULL to ignore errors.}
64+ function initWithContentsOfURL_error (url: NSURL; outError: NSErrorPtr): id;
65+ message 'initWithContentsOfURL:error:';
66+ {Plays a sound asynchronously. Calling this method implicitly calls the
67+ prepareToPlay() method if the audio player is not already prepared to play.}
68+ function play: Boolean; message 'play';
69+
70+ {A value of 0.0 indicates silence; a value of 1.0 (the default) indicates
71+ full volume for the audio player instance. Use this property to control an
72+ audio player’s volume relative to other audio output.}
73+ function volume: Single; message 'volume';
74+ procedure setVolume(newValue: Single); message 'setVolume:';
75+
76+ {A value of 0, which is the default, means to play the sound once.
77+ Set a positive integer value to specify the number of times to return
78+ to the start and play again. For example, specifying a value of 1
79+ results in a total of two plays of the sound. Set any negative
80+ integer value to loop the sound indefinitely until you call the
81+ stop() method.}
82+ function numberOfLoops: Integer; message 'numberOfLoops';
83+ procedure setNumberOfLoops(newValue: Integer); message 'setNumberOfLoops:';
84+
85+ {A Boolean value that indicates whether the audio player is playing (true)
86+ or not (false).}
87+ function isPlaying: Boolean; message 'isPlaying';
88+
89+ {Calling this method preloads buffers and acquires the audio hardware
90+ needed for playback, which minimises the lag between calling the play()
91+ method and the start of sound output.}
92+ function prepareToPlay: Boolean; message 'prepareToPlay';
93+
94+ {Pauses playback; sound remains ready to resume playback from where
95+ it left off. It does not release the audio hardware that was acquired
96+ on calling play() or prepareToPlay().}
97+ procedure pause; message 'pause';
98+
99+ {The stop method does not reset the value of the currentTime property to 0.
100+ If you call stop during playback and then call play(), playback resumes
101+ at the point where it left off. Callikng this method undoes the setup
102+ performed on calling the play() or prepareToPlay() methods.}
103+ procedure stop; message 'stop';
104+
105+ {The total duration, in seconds, of the sound associated with the audio
106+ player.}
107+ function duration: Double; message 'duration';
108+
109+ {If the sound is playing, currentTime is the offset of the current playback
110+ position, measured in seconds from the start of the sound. If the sound is
111+ not playing, currentTime is the offset of where playing starts upon calling
112+ the play() method, measured in seconds from the start of the sound.
113+ By setting this property you can seek to a specific point in a sound file or
114+ implement audio fast-forward and rewind functions.}
115+ function currentTime: Double; message 'currentTime';
116+ procedure setCurrentTime(newValue: Double); message 'setCurrentTime:';
117+
118+ {To enable adjustable playback rate for an audio player, set this property
119+ to true after you initialize the player and before you call the
120+ prepareToPlay() instance method for the player.}
121+ function enableRate: Boolean; message 'enableRate';
122+ procedure setEnableRate(newValue:Boolean); message 'setEnableRate:';
123+
124+ {This property’s default value of 1.0 provides normal playback rate. The
125+ available range is from 0.5 for half-speed playback through 2.0 for
126+ double-speed playback.
127+ To set an audio player’s playback rate, you must first enable rate
128+ adjustment as described in the enableRate property description.}
129+ function rate: Single; message 'rate';
130+ procedure setRate(newValue: Single); message 'setRate:';
131+
132+ {The UID of the current audio player.}
133+ function currentDevice: NSString; message 'currentDevice';
134+
135+ {Number of audio channels in the sound associated with the audio player.}
136+ function numberOfChannels: Integer; message 'numberOfChannels';
137+
138+ {The default value for the meteringEnabled property is off (Boolean false).
139+ Before using metering for an audio player, you need to enable it by
140+ setting this property to true.}
141+ function isMeteringEnabled: Boolean; message 'isMeteringEnabled';
142+ procedure setMeteringEnabled(newValue:Boolean); message 'setMeteringEnabled:';
143+
144+ {To obtain current audio power values, you must call this method before
145+ calling averagePowerForChannel or peakPowerForChannel}
146+ procedure updateMeters; message 'updateMeters';
147+
148+ {Returns the average power for a given channel, in decibels, for the sound
149+ being played.
150+ Channel numbers are zero-indexed. A monaural signal, or the left channel
151+ of a stereo signal, has channel number 0.
152+ A return value of 0 dB indicates full scale, or maximum power; a return
153+ value of –160 dB indicates minimum power (that is, near silence). If the
154+ signal provided to the audio player exceeds ±full scale, then the return
155+ value may exceed 0 (that is, it may enter the positive range).}
156+ function avgPwrForChannel(newValue:LongWord): Double; message 'averagePowerForChannel:';
157+
158+ {Returns the peak power for a given channel, in decibels, for the sound
159+ being played.
160+ Channel numbers are zero-indexed. A monaural signal, or the left channel
161+ of a stereo signal, has channel number 0.
162+ A return value of 0 dB indicates full scale, or maximum power; a return
163+ value of –160 dB indicates minimum power (that is, near silence). If the
164+ signal provided to the audio player exceeds ±full scale, then the return
165+ value may exceed 0 (that is, it may enter the positive range).}
166+ function peakPwrForChannel(newValue:LongWord): Double; message 'peakPowerForChannel:';
167+
168+ procedure setDelegate(newValue: AVAudioPlayerDelegateProtocol); message 'setDelegate:';
169+ function delegate: AVAudioPlayerDelegateProtocol; message 'delegate';
170+ end;
171+
172+ AVAudioPlayerDelegateProtocol = objcprotocol external name 'AVAudioPlayerDelegate' (NSObjectProtocol)
173+ optional
174+ procedure audioPlayerDidFinishPlaying_successfully (player: AVAudioPlayer; flag: ObjCBOOL); message 'audioPlayerDidFinishPlaying:successfully:';
175+ procedure audioPlayerDecodeErrorDidOccur_error (player: AVAudioPlayer; error: NSError); message 'audioPlayerDecodeErrorDidOccur:error:';
176+ end;
177+
178+ { TMyDelegate }
179+ TMyDelegate = objcclass(NSObject, AVAudioPlayerDelegateProtocol)
180+ public
181+ procedure audioPlayerDidFinishPlaying_successfully(player: AVAudioPlayer; flag: ObjCBOOL); message 'audioPlayerDidFinishPlaying:successfully:';
182+ //procedure audioPlayerDecodeErrorDidOccur_error(player: AVAudioPlayer; error: NSError); message 'audioPlayerDecodeErrorDidOccur:error:';
183+ end;
184+
185+ TForm1 = class(TForm)
186+ DurationLabel: TLabel;
187+ Timer1: TTimer;
188+ TitleLabel: TLabel;
189+ ResumeButton: TButton;
190+ StopButton: TButton;
191+ PauseButton: TButton;
192+ PlayButton: TButton;
193+ TrackBar1: TTrackBar;
194+ procedure PauseButtonClick(Sender: TObject);
195+ procedure PlayButtonClick(Sender: TObject);
196+ procedure ResumeButtonClick(Sender: TObject);
197+ procedure StopButtonClick(Sender: TObject);
198+ procedure Timer1Update(Sender: TObject);
199+
200+ private
201+
202+ public
203+
204+ end;
205+
206+var
207+ Form1: TForm1;
208+ MyAudioPlayer : AVAudioPlayer = Nil;
209+ fileDuration: NSTimeInterval = 0;
210+ myDelegate: TMyDelegate = Nil;
211+
212+implementation
213+
214+{$R *.lfm}
215+
216+procedure TMyDelegate.audioPlayerDidFinishPlaying_successfully(player: AVAudioPlayer; flag: ObjCBool);
217+begin
218+ if(flag) then
219+ begin
220+ // Set button states
221+ Form1.PlayButton.Enabled := True;
222+ Form1.PauseButton.Enabled := False;
223+ Form1.ResumeButton.Enabled := False;
224+ Form1.StopButton.Enabled := False;
225+ end;
226+end;
227+
228+// Play audio procedure
229+procedure PlayAudio(audioFileName : NSString; numberOfLoops: Integer);
230+var
231+ path: NSString;
232+ url : NSURL;
233+ err : NSError;
234+begin
235+ // Path to your application bundle's resource directory
236+ // with the filename appended
237+ path := NSBundle.mainBundle.resourcePath.stringByAppendingPathComponent(audioFileName);
238+ url := NSURL.fileURLWithPath(path);
239+
240+ // Setup audio player with file
241+ MyAudioPlayer := AVAudioPlayer.alloc.initWithContentsOfURL_error(url, @err);
242+
243+ // Setup labels
244+ Form1.TitleLabel.Caption := 'Audio filename: Minuet in G.mp3';
245+ Form1.DurationLabel.Caption := 'Duration: ' + FloatToStr(Trunc(MyAudioPlayer.duration)) + ' seconds';
246+
247+ // Setup delegate
248+ myDelegate := TMyDelegate.alloc.init;
249+ MyAudioPlayer.setDelegate(myDelegate);
250+
251+ // Setup and play
252+ if Assigned(MyAudioPlayer) then
253+ begin
254+ Form1.TrackBar1.Max := Trunc(MyAudioPlayer.duration);
255+ MyAudioPlayer.setNumberOfLoops(numberOfLoops);
256+ Form1.Timer1.Enabled := True;
257+ MyAudioPlayer.play
258+ end
259+ else
260+ // Use the Applications > Utilities > Console application to find error messages
261+ NSLog(NSStr('Error in procedure PlayAudio(): %@'), err);
262+end;
263+
264+procedure TForm1.PlayButtonClick(Sender: TObject);
265+begin
266+ // Destroy the existing audio player if any before
267+ // creating another in PlayAudio procedure
268+ if Assigned(MyAudioPlayer) then
269+ begin
270+ MyAudioPlayer.release;
271+ MyAudioPlayer := Nil;
272+ end;
273+
274+ // Set button states
275+ PlayButton.Enabled := False;
276+ ResumeButton.Enabled := False;
277+ PauseButton.Enabled := True;
278+ StopButton.Enabled := True;
279+
280+ // Play audio file located in your application bundle Resources directory
281+ // This particular demo file can be found on your Mac (up to Mojave) at:
282+ // /Applications/iPhoto.app/Contents/Resources/Music/Minuet in G.mp3
283+ // and should be copied to your application bundle Resources directory.
284+ PlayAudio(NSStr('Minuet in G.mp3'), 0);
285+end;
286+
287+// Pause audio
288+procedure TForm1.PauseButtonClick(Sender: TObject);
289+begin
290+ if(MyAudioPlayer.isPlaying) then
291+ begin
292+ MyAudioPlayer.Pause;
293+
294+ // Stop timer updating track bar
295+ Timer1.Enabled := False;
296+
297+ // Set button states
298+ PlayButton.Enabled := False;
299+ PauseButton.Enabled := False;
300+ ResumeButton.Enabled := True;
301+ StopButton.Enabled := False;
302+ end;
303+end;
304+
305+// Resume audio
306+procedure TForm1.ResumeButtonClick(Sender: TObject);
307+begin
308+ // Note: This will restart the audio where it left off after a pause
309+ // unless audio has been reset to zero
310+ // Do not create yet another audio player by calling PlayAudio() again :-)
311+ if(MyAudioPlayer.currentTime <> 0) then
312+ begin
313+ MyAudioPlayer.play;
314+
315+ // Re-start timer updating track bar
316+ Timer1.Enabled := True;
317+
318+ // Set button states
319+ PlayButton.Enabled := False;
320+ ResumeButton.Enabled := False;
321+ PauseButton.Enabled := True;
322+ StopButton.Enabled := True;
323+ end;
324+end;
325+
326+// Stop audio
327+procedure TForm1.StopButtonClick(Sender: TObject);
328+begin
329+ // if playing then stop, otherwise ignore
330+ if(MyAudioPlayer.IsPlaying) then
331+ begin
332+ MyAudioPlayer.stop; // audio can still be resumed, so...
333+ MyAudioPlayer.setCurrentTime(0); // reset audio to start
334+
335+ // Set button states
336+ PlayButton.Enabled := True;
337+ PauseButton.Enabled := False;
338+ ResumeButton.Enabled := False;
339+ StopButton.Enabled := False;
340+
341+ // Stop timer, reset trackbar position and duration
342+ Timer1.Enabled := False;
343+ TrackBar1.Position := 0;
344+ DurationLabel.Caption := 'Duration: ' + FloatToStr(Trunc(MyAudioPlayer.duration)) + ' seconds';
345+ end;
346+end;
347+
348+procedure TForm1.Timer1Update(Sender: TObject);
349+begin
350+ // Update trackbar pos and duration
351+ TrackBar1.Position := Trunc(MyAudioPlayer.currentTime);
352+ DurationLabel.Caption := 'Duration: ' + FloatToStr(Trunc(MyAudioPlayer.Duration - MyAudioPlayer.currentTime)) + ' seconds';
353+end;
354+
355+finalization
356+
357+// Cleanup
358+If (Assigned (MyAudioPlayer)) then
359+ begin
360+ MyAudioPlayer.Release;
361+ MyDelegate.Release;
362+ MyAudioPlayer := Nil;
363+ end;
364+
365+end.
366+```
367+
368+The full project source code for this application is available from [SourceForge](https://sourceforge.net/p/lazarus-wiki-projects/code/ci/master/tree/laz_avaudioplayer/).
369+
370+## See also
371+
372+- [[macOS Play Alert Sound|System Sound Services]]
373+- [[macOS Audio Recorder]]
374+- [[macOS Video Player]] - also plays audio (especially streaming audio).
375+- [[macOS MIDI Player]]
376+- [[macOS NSSound]]
377+- [[macOS Sound Utilities]]
378+- [[Multimedia Programming]]
379+
380+## External links
381+
382+- [Apple: Working with Audio](https://developer.apple.com/audio/)
383+- [Apple: AVFoundation Framework](https://developer.apple.com/documentation/avfoundation)
384+- [Apple: AVAudioPlayer](https://developer.apple.com/documentation/avfoundation/avaudioplayer)
385+
386+---
387+*From the Free Pascal wiki, page "macOS Audio Player", as it stood on 2022-01-22 after a change by Trev, out of the wiki's dump of 2024-05-26. Copyright stays with the author who wrote it, according to the old wiki's [[Licensing]] page. If you have concerns about this material, [message the admin](/wiki-contact).*
388+Category: Audio
389+Category: Code
390+Category: Example programs
391+Category: Multimedia
392+Category: macOS

Back to history