Skip to content
This repository was archived by the owner on Apr 29, 2024. It is now read-only.

Commit e0c7426

Browse files
committed
Add new sample - AudioQueueOfflineRenderDemo
1 parent e53ee57 commit e0c7426

14 files changed

+491
-0
lines changed
+258
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq;
4+
using System.Runtime.InteropServices;
5+
6+
using MonoTouch;
7+
using MonoTouch.AVFoundation;
8+
using MonoTouch.Foundation;
9+
using MonoTouch.UIKit;
10+
using MonoTouch.Dialog;
11+
using MonoTouch.AudioToolbox;
12+
using MonoTouch.AudioUnit;
13+
14+
using MonoTouch.CoreFoundation;
15+
16+
using AudioFileID = System.IntPtr;
17+
18+
namespace AudioQueueOfflineRenderDemo
19+
{
20+
[Register ("AppDelegate")]
21+
public unsafe partial class AppDelegate : UIApplicationDelegate
22+
{
23+
UIWindow window;
24+
DialogViewController dvc;
25+
StyledStringElement element;
26+
bool busy;
27+
AVAudioPlayer player;
28+
29+
public override bool FinishedLaunching (UIApplication app, NSDictionary options)
30+
{
31+
dvc = new DialogViewController (new RootElement ("Audio Queue Offline Render Demo") {
32+
new Section ("Audio Queue Offline Render Demo") {
33+
(element = new StyledStringElement ("Render audio", RenderAudioAsync) { Alignment = UITextAlignment.Center }),
34+
}
35+
});
36+
37+
element.Alignment = UITextAlignment.Center;
38+
39+
window = new UIWindow (UIScreen.MainScreen.Bounds);
40+
window.RootViewController = dvc;
41+
window.MakeKeyAndVisible ();
42+
43+
return true;
44+
}
45+
46+
void SetCaption (string caption)
47+
{
48+
BeginInvokeOnMainThread (() =>
49+
{
50+
element.Caption = caption;
51+
dvc.ReloadData ();
52+
});
53+
}
54+
55+
void RenderAudioAsync ()
56+
{
57+
CFUrl sourceUrl = CFUrl.FromFile (NSBundle.MainBundle.PathForResource ("composeaudio", "mp3"));
58+
CFUrl destinationUrl = CFUrl.FromFile (System.IO.Path.Combine (System.Environment.GetFolderPath (Environment.SpecialFolder.Personal), "output.caf"));
59+
60+
if (busy)
61+
return;
62+
63+
busy = true;
64+
SetCaption ("Rendering...");
65+
66+
var thread = new System.Threading.Thread (() =>
67+
{
68+
try {
69+
RenderAudio (sourceUrl, destinationUrl);
70+
} catch (Exception ex) {
71+
Console.WriteLine (ex);
72+
}
73+
BeginInvokeOnMainThread (() =>
74+
{
75+
SetCaption ("Playing...");
76+
77+
using (var playUrl = new NSUrl (destinationUrl.Handle)) {
78+
player = AVAudioPlayer.FromUrl (playUrl);
79+
}
80+
81+
player.Play ();
82+
player.FinishedPlaying += (sender, e) =>
83+
{
84+
BeginInvokeOnMainThread (() =>
85+
{
86+
player.Dispose ();
87+
player = null;
88+
SetCaption ("Render audio");
89+
busy = false;
90+
});
91+
};
92+
});
93+
});
94+
thread.IsBackground = true;
95+
thread.Start ();
96+
}
97+
98+
static void CalculateBytesForTime (AudioStreamBasicDescription desc, int maxPacketSize, double seconds, out int bufferSize, out int packetCount)
99+
{
100+
const int maxBufferSize = 0x10000;
101+
const int minBufferSize = 0x4000;
102+
103+
if (desc.FramesPerPacket > 0) {
104+
bufferSize = (int) (desc.SampleRate / desc.FramesPerPacket * seconds * maxPacketSize);
105+
} else {
106+
bufferSize = maxBufferSize > maxPacketSize ? maxBufferSize : maxPacketSize;
107+
}
108+
109+
if (bufferSize > maxBufferSize && bufferSize > maxPacketSize) {
110+
bufferSize = maxBufferSize;
111+
} else if (bufferSize < minBufferSize) {
112+
bufferSize = minBufferSize;
113+
}
114+
115+
packetCount = bufferSize / maxPacketSize;
116+
}
117+
118+
unsafe static void HandleOutput (AudioFile audioFile, AudioQueue queue, AudioQueueBuffer *audioQueueBuffer, ref int packetsToRead, ref long currentPacket, ref bool done, ref bool flushed, ref AudioStreamPacketDescription [] packetDescriptions)
119+
{
120+
int bytes;
121+
int packets;
122+
123+
if (done)
124+
return;
125+
126+
packets = packetsToRead;
127+
bytes = (int) audioQueueBuffer->AudioDataBytesCapacity;
128+
129+
packetDescriptions = audioFile.ReadPacketData (false, currentPacket, ref packets, audioQueueBuffer->AudioData, ref bytes);
130+
131+
if (packets > 0) {
132+
audioQueueBuffer->AudioDataByteSize = (uint) bytes;
133+
queue.EnqueueBuffer (audioQueueBuffer, packetDescriptions);
134+
currentPacket += packets;
135+
} else {
136+
if (!flushed) {
137+
queue.Flush ();
138+
flushed = true;
139+
}
140+
141+
queue.Stop (false);
142+
done = true;
143+
}
144+
}
145+
146+
unsafe static void RenderAudio (CFUrl sourceUrl, CFUrl destinationUrl)
147+
{
148+
AudioStreamBasicDescription dataFormat;
149+
AudioQueueBuffer *buffer = null;
150+
long currentPacket = 0;
151+
int packetsToRead = 0;
152+
AudioStreamPacketDescription [] packetDescs = null;
153+
bool flushed = false;
154+
bool done = false;
155+
int bufferSize;
156+
157+
using (var audioFile = AudioFile.Open (sourceUrl, AudioFilePermission.Read, (AudioFileType) 0)) {
158+
dataFormat = audioFile.StreamBasicDescription;
159+
160+
using (var queue = new OutputAudioQueue (dataFormat, CFRunLoop.Current, CFRunLoop.CFRunLoopCommonModes)) {
161+
queue.OutputCompleted += (sender, e) =>
162+
{
163+
HandleOutput (audioFile, queue, buffer, ref packetsToRead, ref currentPacket, ref done, ref flushed, ref packetDescs);
164+
};
165+
166+
// we need to calculate how many packets we read at a time and how big a buffer we need
167+
// we base this on the size of the packets in the file and an approximate duration for each buffer
168+
bool isVBR = dataFormat.BytesPerPacket == 0 || dataFormat.FramesPerPacket == 0;
169+
170+
// first check to see what the max size of a packet is - if it is bigger
171+
// than our allocation default size, that needs to become larger
172+
// adjust buffer size to represent about a second of audio based on this format
173+
CalculateBytesForTime (dataFormat, audioFile.MaximumPacketSize, 1.0, out bufferSize, out packetsToRead);
174+
175+
if (isVBR) {
176+
packetDescs = new AudioStreamPacketDescription [packetsToRead];
177+
} else {
178+
packetDescs = null; // we don't provide packet descriptions for constant bit rate formats (like linear PCM)
179+
}
180+
181+
if (audioFile.MagicCookie.Length != 0)
182+
queue.MagicCookie = audioFile.MagicCookie;
183+
184+
// allocate the input read buffer
185+
queue.AllocateBuffer (bufferSize, out buffer);
186+
187+
// prepare the capture format
188+
AudioStreamBasicDescription captureFormat;
189+
captureFormat.SampleRate = dataFormat.SampleRate;
190+
captureFormat.Format = AudioFormatType.LinearPCM;
191+
captureFormat.FormatFlags = AudioFormatFlags.IsSignedInteger | AudioFormatFlags.IsPacked |
192+
(AudioFormatFlags) (24 << (int) AudioFormatFlags.LinearPCMSampleFractionShift);
193+
captureFormat.ChannelsPerFrame = dataFormat.ChannelsPerFrame;
194+
captureFormat.FramesPerPacket = 1;
195+
captureFormat.BitsPerChannel = 32;
196+
captureFormat.BytesPerPacket = dataFormat.ChannelsPerFrame * 4;
197+
captureFormat.BytesPerFrame = captureFormat.BytesPerPacket;
198+
199+
queue.SetOfflineRenderFormat (captureFormat, audioFile.ChannelLayout);
200+
201+
// prepare the target format
202+
AudioStreamBasicDescription dstFormat;
203+
dstFormat.SampleRate = dataFormat.SampleRate;
204+
dstFormat.ChannelsPerFrame = dataFormat.ChannelsPerFrame;
205+
dstFormat.Format = AudioFormatType.LinearPCM;
206+
dstFormat.FormatFlags = AudioFormatFlags.IsPacked | AudioFormatFlags.LinearPCMIsSignedInteger;
207+
dstFormat.BitsPerChannel = 16;
208+
dstFormat.BytesPerPacket = 2 * dstFormat.ChannelsPerFrame;
209+
dstFormat.BytesPerFrame = dstFormat.BytesPerPacket;
210+
dstFormat.FramesPerPacket = 1;
211+
212+
using (var captureFile = ExtAudioFile.CreateWithUrl (destinationUrl, AudioFileType.CAF, dstFormat, AudioFileFlags.EraseFlags)) {
213+
captureFile.ClientDataFormat = captureFormat;
214+
215+
int captureBufferSize = bufferSize / 2;
216+
var captureABL = new AudioBufferList (1);
217+
218+
AudioQueueBuffer *captureBuffer;
219+
queue.AllocateBuffer (captureBufferSize, out captureBuffer);
220+
221+
captureABL.Buffers [0].Data = captureBuffer->AudioData;
222+
captureABL.Buffers [0].NumberChannels = captureFormat.ChannelsPerFrame;
223+
224+
queue.Start ();
225+
226+
double ts = 0;
227+
queue.RenderOffline (ts, captureBuffer, 0);
228+
229+
HandleOutput (audioFile, queue, buffer, ref packetsToRead, ref currentPacket, ref done, ref flushed, ref packetDescs);
230+
231+
while (true) {
232+
int reqFrames = captureBufferSize / captureFormat.BytesPerFrame;
233+
234+
queue.RenderOffline (ts, captureBuffer, reqFrames);
235+
236+
captureABL.Buffers [0].Data = captureBuffer->AudioData;
237+
captureABL.Buffers [0].DataByteSize = (int) captureBuffer->AudioDataByteSize;
238+
int writeFrames = captureABL.Buffers [0].DataByteSize / captureFormat.BytesPerFrame;
239+
240+
// Console.WriteLine ("ts: {0} AudioQueueOfflineRender: req {1} frames / {2} bytes, got {3} frames / {4} bytes",
241+
// ts, reqFrames, captureBufferSize, writeFrames, captureABL.Buffers [0].DataByteSize);
242+
243+
captureFile.WriteAsync (writeFrames, captureABL);
244+
245+
if (flushed)
246+
break;
247+
248+
ts += writeFrames;
249+
}
250+
251+
CFRunLoop.Current.RunInMode (CFRunLoop.CFDefaultRunLoopMode, 1, false);
252+
}
253+
}
254+
}
255+
}
256+
}
257+
}
258+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
3+
<PropertyGroup>
4+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5+
<Platform Condition=" '$(Platform)' == '' ">iPhoneSimulator</Platform>
6+
<ProductVersion>10.0.0</ProductVersion>
7+
<SchemaVersion>2.0</SchemaVersion>
8+
<ProjectGuid>{8D016236-ED61-445E-A11F-31DE286E1AAD}</ProjectGuid>
9+
<ProjectTypeGuids>{6BC8ED88-2882-458C-8E55-DFD12B67127B};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
10+
<OutputType>Exe</OutputType>
11+
<RootNamespace>AudioQueueOfflineRenderDemo</RootNamespace>
12+
<AssemblyName>AudioQueueOfflineRenderDemo</AssemblyName>
13+
</PropertyGroup>
14+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhoneSimulator' ">
15+
<DebugSymbols>true</DebugSymbols>
16+
<DebugType>full</DebugType>
17+
<Optimize>false</Optimize>
18+
<OutputPath>bin\iPhoneSimulator\Debug</OutputPath>
19+
<DefineConstants>DEBUG;</DefineConstants>
20+
<ErrorReport>prompt</ErrorReport>
21+
<WarningLevel>4</WarningLevel>
22+
<ConsolePause>false</ConsolePause>
23+
<MtouchDebug>true</MtouchDebug>
24+
<MtouchProfiling>true</MtouchProfiling>
25+
<MtouchLink>None</MtouchLink>
26+
<MtouchI18n />
27+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
28+
<EnvironmentVariables>
29+
<EnvironmentVariables>
30+
<Variable name="MONO_LOG_LEVEL" value="debug" />
31+
<Variable name="MONO_LOG_MASK" value="dll" />
32+
</EnvironmentVariables>
33+
</EnvironmentVariables>
34+
<CrashReportingApiKey />
35+
</PropertyGroup>
36+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhoneSimulator' ">
37+
<DebugType>none</DebugType>
38+
<Optimize>true</Optimize>
39+
<OutputPath>bin\iPhoneSimulator\Release</OutputPath>
40+
<ErrorReport>prompt</ErrorReport>
41+
<WarningLevel>4</WarningLevel>
42+
<ConsolePause>false</ConsolePause>
43+
<MtouchLink>None</MtouchLink>
44+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
45+
</PropertyGroup>
46+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|iPhone' ">
47+
<DebugSymbols>true</DebugSymbols>
48+
<DebugType>full</DebugType>
49+
<Optimize>false</Optimize>
50+
<OutputPath>bin\iPhone\Debug</OutputPath>
51+
<DefineConstants>DEBUG;</DefineConstants>
52+
<ErrorReport>prompt</ErrorReport>
53+
<WarningLevel>4</WarningLevel>
54+
<ConsolePause>false</ConsolePause>
55+
<CodesignKey>iPhone Developer</CodesignKey>
56+
<MtouchDebug>true</MtouchDebug>
57+
<MtouchProfiling>true</MtouchProfiling>
58+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
59+
<CrashReportingApiKey />
60+
<MtouchExtraArgs>-v -v -v</MtouchExtraArgs>
61+
<MtouchI18n />
62+
<IpaPackageName />
63+
</PropertyGroup>
64+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|iPhone' ">
65+
<DebugType>none</DebugType>
66+
<Optimize>true</Optimize>
67+
<OutputPath>bin\iPhone\Release</OutputPath>
68+
<ErrorReport>prompt</ErrorReport>
69+
<WarningLevel>4</WarningLevel>
70+
<ConsolePause>false</ConsolePause>
71+
<CodesignKey>iPhone Developer</CodesignKey>
72+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
73+
</PropertyGroup>
74+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Ad-Hoc|iPhone' ">
75+
<DebugType>none</DebugType>
76+
<Optimize>true</Optimize>
77+
<OutputPath>bin\iPhone\Ad-Hoc</OutputPath>
78+
<ErrorReport>prompt</ErrorReport>
79+
<WarningLevel>4</WarningLevel>
80+
<ConsolePause>false</ConsolePause>
81+
<BuildIpa>true</BuildIpa>
82+
<CodesignKey>iPhone Distribution</CodesignKey>
83+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
84+
</PropertyGroup>
85+
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'AppStore|iPhone' ">
86+
<DebugType>none</DebugType>
87+
<Optimize>true</Optimize>
88+
<OutputPath>bin\iPhone\AppStore</OutputPath>
89+
<ErrorReport>prompt</ErrorReport>
90+
<WarningLevel>4</WarningLevel>
91+
<ConsolePause>false</ConsolePause>
92+
<CodesignKey>iPhone Distribution</CodesignKey>
93+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
94+
</PropertyGroup>
95+
<ItemGroup>
96+
<Reference Include="System" />
97+
<Reference Include="System.Xml" />
98+
<Reference Include="System.Core" />
99+
<Reference Include="monotouch" />
100+
<Reference Include="MonoTouch.Dialog-1" />
101+
</ItemGroup>
102+
<ItemGroup>
103+
<None Include="Info.plist" />
104+
<None Include="README.md" />
105+
</ItemGroup>
106+
<ItemGroup>
107+
<Compile Include="Main.cs" />
108+
<Compile Include="AppDelegate.cs" />
109+
</ItemGroup>
110+
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
111+
<ItemGroup>
112+
<Content Include="composeaudio.mp3" />
113+
<Content Include="icons\114_icon.png" />
114+
<Content Include="icons\144_icon.png" />
115+
<Content Include="icons\29_icon.png" />
116+
<Content Include="icons\50_icon.png" />
117+
<Content Include="icons\57_icon.png" />
118+
<Content Include="icons\58_icon.png" />
119+
<Content Include="icons\72_icon.png" />
120+
</ItemGroup>
121+
<ItemGroup>
122+
<Folder Include="icons\" />
123+
</ItemGroup>
124+
</Project>

0 commit comments

Comments
 (0)