220 hertz sine doesn't sound correct - objective-c

I have the following Objective-C code that generates a 220hz tone, however, it doesn't sound like other 220hz tones, for example Wikipedia has a 220 tone here: https://en.wikipedia.org/wiki/File:220_Hz_sine_wave.ogg that sounds very low, mine sounds really high pitched in comparison...any ideas?
- (void)render:(AVAudioPCMBuffer *)bufferInput frequency:(float)frequency amplitude:(float)amplitude
{
const int channels = 2;
int inNumberFrames = bufferInput.frameLength;
float *const *floatChannelData = bufferInput.floatChannelData;
for (int i = 0; i < inNumberFrames ; i ++) {
float theta = frequency * i * 2.0 * M_PI / 44100;
float value = sinf(theta);
for (int channelNumber = 0; channelNumber < channels ; channelNumber++) {
float * const channelBuffer = floatChannelData[channelNumber];
channelBuffer[i] = value * amplitude;
}
}
}

Related

Vulkan: Loading floating point cubemap textures distorted

I am using vulkan-tutorial codes and i made modify for cubemap.
when i use VK_FORMAT_R8G8B8A8_UNORM is working with this code:
unsigned char* pixelsArray[6];
for (int i = 0; i < 6; ++i)
{
pixelsArray[i] = stbi_load(imageFileArray[i].c_str(), &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
}
VkDeviceSize allSize = texWidth * texHeight * 4 * 6;
VkDeviceSize size = texWidth * texHeight * 4 ;
VkBufferCreateInfo bufferInfo{};
...
bufferInfo.size = allSize ;
vkMapMemory(device, stagingBufferMemory, 0, AllSize, 0, &data);
for(int i = 0; i < 6; ++i)
{
memcpy( (char*) data + (size*i) , pixelsArray[i], static_cast<size_t>(size));
}
vkUnmapMemory(device, stagingBufferMemory);
VkImageCreateInfo imageInfo{};
...
imageInfo.arrayLayers = 6;
imageInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
imageInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
VkImageViewCreateInfo viewInfo{};
...
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
viewInfo.format = VK_FORMAT_R8G8B8A8_UNORM;
viewInfo.subresourceRange.layerCount = 6;
but when i try VK_FORMAT_R16G16B16A16_SFLOAT is giving distorted display and no validation error with this code:
float* pixelsArray[6];
for (int i = 0; i < 6; ++i)
{
pixelsArray[i] = stbi_loadf(imageFileArray[i].c_str(), &texWidth, &texHeight, &texChannels, STBI_rgb_alpha);
}
VkDeviceSize allSize = texWidth * texHeight * 4 * 6 * 2;// I added *2
VkDeviceSize size = texWidth * texHeight * 4 * 2;// I added *2
VkBufferCreateInfo bufferInfo{};
...
bufferInfo.size = allSize ;
vkMapMemory(device, stagingBufferMemory, 0, AllSize, 0, &data);
for(int i = 0; i < 6; ++i)
{
memcpy( (char*) data + (size*i) , pixelsArray[i], static_cast<size_t>(size));
}
vkUnmapMemory(device, stagingBufferMemory);
VkImageCreateInfo imageInfo{};
...
imageInfo.arrayLayers = 6;
imageInfo.format = VK_FORMAT_R16G16B16A16_SFLOAT;
imageInfo.flags = VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT;
VkImageViewCreateInfo viewInfo{};
...
viewInfo.viewType = VK_IMAGE_VIEW_TYPE_CUBE;
viewInfo.format = VK_FORMAT_R16G16B16A16_SFLOAT;
viewInfo.subresourceRange.layerCount = 6;
when VK_FORMAT_R8G8B8A8_UNORM :
when VK_FORMAT_R16G16B16A16_SFLOAT :
i fixed the problem. problem was that i want to use half float but i was sending float to memcpy function.i searched how can i use half float and i found a solution without using extra library.
what i did add helper functions :
typedef unsigned int uint;
typedef unsigned short ushort;
uint as_uint(const float x)
{
return *(uint*)&x;
}
ushort float_to_half(const float x)
{
// IEEE-754 16-bit floating-point format (without infinity): 1-5-10, exp-15, +-131008.0, +-6.1035156E-5, +-5.9604645E-8, 3.311 digits
const uint b = as_uint(x)+0x00001000; // round-to-nearest-even: add last bit after truncated mantissa
const uint e = (b&0x7F800000)>>23; // exponent
const uint m = b&0x007FFFFF; // mantissa; in line below: 0x007FF000 = 0x00800000-0x00001000 = decimal indicator flag - initial rounding
return (b&0x80000000)>>16 | (e>112)*((((e-112)<<10)&0x7C00)|m>>13) | ((e<113)&(e>101))*((((0x007FF000+m)>>(125-e))+1)>>1) | (e>143)*0x7FFF; // sign : normalized : denormalized : saturate
}
and fix problem with this helper functions :
VkDeviceSize size_2 = texWidth * texHeight * 4;// different from the above variables in question : allSize or size
//create half float for cubemap
void* half_pixelsArray[6];
half_pixelsArray[0] = new ushort[size_2];
half_pixelsArray[1] = new ushort[size_2];
half_pixelsArray[2] = new ushort[size_2];
half_pixelsArray[3] = new ushort[size_2];
half_pixelsArray[4] = new ushort[size_2];
half_pixelsArray[5] = new ushort[size_2];
//copy from float to half float
for (int i = 0; i < 6; ++i)
{
for (int j = 0; j < size_2; ++j)
{
((ushort*)half_pixelsArray[i])[j] = float_to_half( pixelsArray[i][j] );
}
}
// and change float to half flaot in memcpy
memcpy( (char*) data + (layerSize*i) , half_pixelsArray[i], static_cast<size_t>(layerSize));

numWeights corresponding to mnumVertices?

I have one issue left with ASSIMP DIRECT X C++ ANIMATION WITH SKELETON.
for (UINT m = 0; m < currentMesh->mBones[k]->mNumWeights; m++) //verticer som påverkas
{
vertexVector[k].joints.x = currentMesh->mBones[k]->mWeights[m].mVertexId;
That code shows all vertices affected by a bone - k, inside an iteration.
All of these vertices must have the same vert ID since they are all affected by the same bone/joint.
The problem is, I need to make a list of every vertex and a list of every indice of a face, where I store position, UV, Normal etc.
The list that displays all of the vertices, is not in the same order obviously as the lists that displays all the vertices affected by each bone.
So how can I combine these lists?
"vertexVector"... etc is an example of a list with jointInfo that is corresponding to vertexID.
It has room for more places and another variable for the weight.
But that list doesn't work obviously.
What am I doing wrong with Assimp? Hope this was a clear post.
UPdate this is how i build the matrices: I don't know what is wrong.
void jointTransform(float
timeInSeconds, std::vector<DirectX::XMMATRIX>& transformM, aiAnimation*
ani, UINT nrOfJoints, std::vector<joints>& jointInfo, const aiScene*
scenePtr)
{
DirectX::XMMATRIX iD = DirectX::XMMatrixIdentity();
float ticksPerSecond = (float)ani->mTicksPerSecond;
if (ticksPerSecond == 0)
{
ticksPerSecond = 30;
}
float timeInTicks = timeInSeconds * ticksPerSecond;
float animationTime = fmod(timeInTicks, (float)ani->mDuration);
readNodeHeiarchy(animationTime, scenePtr->mRootNode, iD, jointInfo, ani,
scenePtr);
transformM.resize(nrOfJoints);
for (UINT i = 0; i < transformM.size(); i++)
{
transformM[i] = jointInfo[i].transformFinal;
}
}
void readNodeHeiarchy(float time, const aiNode* node, DirectX::XMMATRIX
parentMat, std::vector<joints>& jointInfo, aiAnimation* ani, const
aiScene* scenePtr)
{
std::string nodeNameString = node->mName.data;
//Skapa en parentTransform från noden. Som sedan skickas in som parent
matris, första gången är det identitetsmatrisen.
aiMatrix4x4 nodeTransform = node->mTransformation;
DirectX::XMMATRIX combined;
combined = DirectX::XMMatrixSet(nodeTransform.a1, nodeTransform.a2,
nodeTransform.a3, nodeTransform.a4,
nodeTransform.b1, nodeTransform.b2, nodeTransform.b3, nodeTransform.b4,
nodeTransform.c1, nodeTransform.c2, nodeTransform.c3, nodeTransform.c4,
nodeTransform.d1, nodeTransform.d2, nodeTransform.d3,
nodeTransform.d4);
const aiNodeAnim* joint = nullptr;
//Kolla om noden är ett ben.
for (UINT i = 0; i < ani->mNumChannels; i++)
{
if (nodeNameString == ani->mChannels[i]->mNodeName.data)
{
joint = ani->mChannels[i];
}
}
DirectX::XMMATRIX globalTransform = DirectX::XMMatrixIdentity();
//om den är ett ben så är joint inte längre nullptr, den blir det benet.
if (joint)
{
DirectX::XMMATRIX S;
DirectX::XMMATRIX R;
DirectX::XMMATRIX T;
//scale
aiVector3D scaleV;
calcLerpScale(scaleV, time, joint);
S = DirectX::XMMatrixScaling(scaleV.x, scaleV.y, scaleV.z);
//rotate
aiQuaternion rotationQ;
calcLerpRot(rotationQ, time, joint);
DirectX::XMVECTOR q;
q = DirectX::XMVectorSet(rotationQ.x, rotationQ.y, rotationQ.z,
rotationQ.w);
R = DirectX::XMMatrixRotationQuaternion(q);
//translate
aiVector3D transV;
calcLerpTrans(transV, time, joint);
T = DirectX::XMMatrixTranslation(transV.x, transV.y, transV.z);
combined = S * R * T;
globalTransform = combined * parentMat;
}
//DirectX::XMMATRIX globalTransform = combined * parentMat;
//if (jointInfo[jointInfo.size() - 1].name.C_Str() != nodeNameString)
//{
for (UINT i = 0; i < jointInfo.size(); i++)
{
if (jointInfo[i].name.C_Str() == nodeNameString)
{
OutputDebugStringA("\n");
OutputDebugStringA(jointInfo[i].name.C_Str());
OutputDebugStringA("\n");
aiMatrix4x4 off = jointInfo[i].offsetM;
DirectX::XMMATRIX offset;
offset = DirectX::XMMatrixSet(off.a1, off.a2, off.a3, off.a4,
off.b1, off.b2, off.b3, off.b4,
off.c1, off.c2, off.c3, off.c4,
off.d1, off.d2, off.d3, off.d4);
DirectX::XMMATRIX rootMInv;
aiMatrix4x4 rootInv = scenePtr->mRootNode-
>mTransformation.Inverse();
rootMInv = DirectX::XMMatrixSet(rootInv.a1, rootInv.a2,
rootInv.a3, rootInv.a4,
rootInv.b1, rootInv.b2, rootInv.b3, rootInv.b4,
rootInv.c1, rootInv.c2, rootInv.c3, rootInv.c4,
rootInv.d1, rootInv.d2, rootInv.d3, rootInv.d4);
jointInfo[i].transformFinal = offset * globalTransform *
rootMInv;
break;
}
}
//}
for (UINT i = 0; i < node->mNumChildren; i++)
{
readNodeHeiarchy(time, node->mChildren[i], globalTransform, jointInfo,
ani, scenePtr);
}
}
void calcLerpScale(aiVector3D& scale, float aniTime, const aiNodeAnim*
joint)
{
if (joint->mNumScalingKeys == 1)
{
scale = joint->mScalingKeys[0].mValue;
return;
}
UINT scaleInd = findIndexS(aniTime, joint);
UINT nextScale = scaleInd + 1;
assert(nextScale < joint->mNumScalingKeys);
float deltaTime = (float)joint->mScalingKeys[nextScale].mTime -
(float)joint->mScalingKeys[scaleInd].mTime;
float factor = (aniTime - (float)joint->mScalingKeys[scaleInd].mTime) /
deltaTime;
assert(factor >= 0.0f && factor <= 1.0f);
const aiVector3D& startScaleV = joint->mScalingKeys[scaleInd].mValue;
const aiVector3D& endScaleV = joint->mScalingKeys[nextScale].mValue;
//interpolate
aiVector3D Delta = endScaleV - startScaleV; // längden
scale = startScaleV + (factor * Delta); //gå ett antal steg beroende på
faktorn mellan start och slut.
scale.Normalize();
}
void calcLerpRot(aiQuaternion& rotation, float aniTime, const aiNodeAnim*
joint)
{
if (joint->mNumRotationKeys == 1)
{
rotation = joint->mRotationKeys[0].mValue;
return;
}
UINT rotIndex = findIndexRot(aniTime, joint);
UINT nextRot = (rotIndex + 1);
assert(nextRot < joint->mNumRotationKeys);
float deltaTime = (float)joint->mRotationKeys[nextRot].mTime -
(float)joint->mRotationKeys[rotIndex].mTime;
float factor = (aniTime - (float)joint->mRotationKeys[rotIndex].mTime) /
deltaTime;
assert(factor >= 0.0f && factor <= 1.0f);
const aiQuaternion& StartRotationQ = joint->mRotationKeys[rotIndex].mValue;
const aiQuaternion& EndRotationQ = joint->mRotationKeys[nextRot].mValue;
aiQuaternion::Interpolate(rotation, StartRotationQ, EndRotationQ, factor);
rotation.Normalize();
}
void calcLerpTrans(aiVector3D& translation, float aniTime, const
aiNodeAnim*
joint)
{
if (joint->mNumPositionKeys == 1)
{
translation = joint->mPositionKeys[0].mValue;
return;
}
UINT transIndex = findIndexT(aniTime, joint);
UINT nextTrans = (transIndex + 1);
assert(nextTrans < joint->mNumPositionKeys);
float deltaTime = (float)joint->mPositionKeys[nextTrans].mTime -
(float)joint->mPositionKeys[transIndex].mTime;
float factor = (aniTime - (float)joint->mPositionKeys[transIndex].mTime) /
deltaTime;
assert(factor >= 0.0f && factor <= 1.0f);
const aiVector3D& startTransV = joint->mPositionKeys[transIndex].mValue;
const aiVector3D& endTransV = joint->mPositionKeys[nextTrans].mValue;
//interpolate
aiVector3D Delta = endTransV - startTransV;
translation = startTransV + (factor * Delta);
translation.Normalize();
}
UINT findIndexRot(float aniTime, const aiNodeAnim* joint)
{
assert(joint->mNumRotationKeys > 0);
for (UINT i = 0; i < joint->mNumRotationKeys - 1; i++)
{
if (aniTime < (float)joint->mRotationKeys[i + 1].mTime)
{
return i;
}
}
assert(0);
}
}
Not sure what you mean by "All of these vertices must have the same vert ID" - the vertex id's of the k:th bone, according to mBones[k]->mWeights[..].mVertexId, are indices to vertices influenced by this bone, and they are going to be different (otherwise there would be either redundancy of conflict).
You probably want to have bone indices and bone weights as part of the vertex definition for easy handling in a shader. Something like
struct vertex (
vec3 pos;
vec3 normal;
float bone_weights[N]; // weights of bones influencing this vertex
unsigned bone_indices[N]; // indices of bones influencing this vertex
}
std::vector<vertex> mesh_vertices;
Where N is the maximum number of influence bones per vertex. A common value is four, but this depends on the mesh your are importing.
Based on your example, a rough draft could be something like this:
// k:th bone of bones in currentMesh
for (UINT m = 0; m < currentMesh->mBones[k]->mNumWeights; m++)
{
float bone_weight = currentMesh->mBones[k]->mWeights[m].mWeight;
unsigned vertex_index = currentMesh->mBones[k]->mWeights[m].mVertexId;
mesh_vertices[vertex_index].bone_weights[m] = bone_weight;
mesh_vertices[vertex_index].bone_indices[m] = k;
}
Here we've assumed that mNumWeights = N, but this needs to checked, as mentioned.

Calculate RSI with postgresql based on language C function from tulip indicator

Is it possible to implement RSI function in the database? https://github.com/TulipCharts/tulipindicators
I have market data inside postgresql table and I would like to calculate RSI on this data. Can we use the code written in language "C"
#include "../indicators.h"
int ti_rsi_start(TI_REAL const *options) {
return (int)options[0];
}
int ti_rsi(int size, TI_REAL const *const *inputs, TI_REAL const *options, TI_REAL *const *outputs) {
const TI_REAL *input = inputs[0];
const int period = (int)options[0];
TI_REAL *output = outputs[0];
const TI_REAL per = 1.0 / ((TI_REAL)period);
if (period < 1) return TI_INVALID_OPTION;
if (size <= ti_rsi_start(options)) return TI_OKAY;
TI_REAL smooth_up = 0, smooth_down = 0;
int i;
for (i = 1; i <= period; ++i) {
const TI_REAL upward = input[i] > input[i-1] ? input[i] - input[i-1] : 0;
const TI_REAL downward = input[i] < input[i-1] ? input[i-1] - input[i] : 0;
smooth_up += upward;
smooth_down += downward;
}
smooth_up /= period;
smooth_down /= period;
*output++ = 100.0 * (smooth_up / (smooth_up + smooth_down));
for (i = period+1; i < size; ++i) {
const TI_REAL upward = input[i] > input[i-1] ? input[i] - input[i-1] : 0;
const TI_REAL downward = input[i] < input[i-1] ? input[i-1] - input[i] : 0;
smooth_up = (upward-smooth_up) * per + smooth_up;
smooth_down = (downward-smooth_down) * per + smooth_down;
*output++ = 100.0 * (smooth_up / (smooth_up + smooth_down));
}
assert(output - outputs[0] == size - ti_rsi_start(options));
return TI_OKAY;
}
You can certainly put this code into a C function, but you'd have to modify the function so that it can be used with PostgreSQL.
Most notably, you have to add the magic block and you have to declare the function and pass arguments and the return value according to the Version 1 Calling Conventions.

How to change lp to mip when using CPLEX callable library

I've solved an lp using CPLEX callable library (in VS2010). The lp is the following:
Maximize
obj: x1 + 2 x2 + 3 x3
Subject To
c1: - x1 + x2 + x3 <= 20
c2: x1 - 3 x2 + x3 <= 30
Bounds
0 <= x1 <= 40
End
The code is given beneath. Now I would like to make it an MIP (additional integrality constraints on the x's). I tried to do so by changing status = CPXlpopt (env, lp); into status = CPXmipopt (env, lp);. This does not work and I get the error 3003: not a mixed-integer problem. Does anybody know what I am missing here?
int main ()
{
/* Declare and allocate space for the variables and arrays where we
will store the optimization results including the status, objective
value, variable values, dual values, row slacks and variable
reduced costs. */
int solstat;
double objval;
double *x = NULL;
double *pi = NULL;
double *slack = NULL;
double *dj = NULL;
CPXENVptr env = NULL;
CPXLPptr lp = NULL;
int status = 0;
int i, j;
int cur_numrows, cur_numcols;
/* Initialize the CPLEX environment */
env = CPXopenCPLEX (&status);
/* Turn on output to the screen */
status = CPXsetintparam (env, CPX_PARAM_SCRIND, CPX_ON);
/* Turn on data checking */
status = CPXsetintparam (env, CPX_PARAM_DATACHECK, CPX_ON);
/* Create the problem. */
lp = CPXcreateprob (env, &status, "lpex1");
/* Now populate the problem with the data. */
#define NUMROWS 2
#define NUMCOLS 3
#define NUMNZ 6
/* To populate by column, we first create the rows, and then add the columns. */
int status = 0;
double obj[NUMCOLS];
double lb[NUMCOLS];
double ub[NUMCOLS];
char *colname[NUMCOLS];
int matbeg[NUMCOLS];
int matind[NUMNZ];
double matval[NUMNZ];
double rhs[NUMROWS];
char sense[NUMROWS];
char *rowname[NUMROWS];
CPXchgobjsen (env, lp, CPX_MAX); /* Problem is maximization */
/* Now create the new rows. First, populate the arrays. */
rowname[0] = "c1";
sense[0] = 'L';
rhs[0] = 20.0;
rowname[1] = "c2";
sense[1] = 'L';
rhs[1] = 30.0;
status = CPXnewrows (env, lp, NUMROWS, rhs, sense, NULL, rowname);
if ( status ) goto TERMINATE;
/* Now add the new columns. First, populate the arrays. */
obj[0] = 1.0; obj[1] = 2.0; obj[2] = 3.0;
matbeg[0] = 0; matbeg[1] = 2; matbeg[2] = 4;
matind[0] = 0; matind[2] = 0; matind[4] = 0;
matval[0] = -1.0; matval[2] = 1.0; matval[4] = 1.0;
matind[1] = 1; matind[3] = 1; matind[5] = 1;
matval[1] = 1.0; matval[3] = -3.0; matval[5] = 1.0;
lb[0] = 0.0; lb[1] = 0.0; lb[2] = 0.0;
ub[0] = 40.0; ub[1] = CPX_INFBOUND; ub[2] = CPX_INFBOUND;
colname[0] = "x1"; colname[1] = "x2"; colname[2] = "x3";
status = CPXaddcols (env, lp, NUMCOLS, NUMNZ, obj, matbeg, matind, matval, lb, ub, colname);
/* Optimize the problem and obtain solution. */
status = CPXlpopt (env, lp);
cur_numrows = CPXgetnumrows (env, lp);
cur_numcols = CPXgetnumcols (env, lp);
x = (double *) malloc (cur_numcols * sizeof(double));
slack = (double *) malloc (cur_numrows * sizeof(double));
dj = (double *) malloc (cur_numcols * sizeof(double));
pi = (double *) malloc (cur_numrows * sizeof(double));
status = CPXsolution (env, lp, &solstat, &objval, x, pi, slack, dj);
/* Write the output to the screen. */
printf ("\nSolution status = %d\n", solstat);
printf ("Solution value = %f\n\n", objval);
for (i = 0; i < cur_numrows; i++) {
printf ("Row %d: Slack = %10f Pi = %10f\n", i, slack[i], pi[i]);
}
for (j = 0; j < cur_numcols; j++) {
printf ("Column %d: Value = %10f Reduced cost = %10f\n",
j, x[j], dj[j]);
}
/* Finally, write a copy of the problem to a file. */
status = CPXwriteprob (env, lp, "lpex1.lp", NULL);
/* Free up the solution */
... (additional code to free up the solution)...
return(status)
}
In your code, you are not declaring any decision variables to be integer. That's why cplex is complaining when you try to solve your problem using a MIP solver. You are doing column-wise modeling and CPXaddcols doesn't have have a parameter for the variable type, but you can use CPXcopyctype or CPXchgctype. Since you the bounds on your decision variables are all greater than 1, you are looking for the 'I' variable type, instead of 'B' for binary.
char *ctype;
ctype = (char *) malloc(cur_numcols * sizeof(char);
for (j = 0; j < cur_numcols; j++) {
ctype[j] = 'I';
}
status = CPXcopyctype(env, lp, ctype);
/* verify status */
status = CPXmipopt (env, lp);
/* verify status */

Objective C Generating (empty) WAV file

I'm trying to save some audio data to a WAV file -- I have audio data that normally I've been using in RemoteIO but I'm now trying to implement a function to save the data. I know the audio data is valid, so that's not a concern -- if I can just get an empty WAV file set up of the correct length, I can fill it with data later.
Right now, the code creates the file and it looks to be the right length in bytes, but apparently it's not formatted correctly, because OSX, QuickTime, iTunes, etc can't recognize it (they see the file, can't determine a length, or play it)
NSURL * tvarFilename = [savePanel URL];
NSLog(#"doSaveAs filename = %#",tvarFilename);
//try to create an audio file there
AudioFileID mRecordFile;
AudioStreamBasicDescription audioFormat;
audioFormat.mSampleRate = 44100.00;
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = 2;
audioFormat.mBitsPerChannel = 16;
audioFormat.mBytesPerPacket = 4;
audioFormat.mBytesPerFrame = 4;
OSStatus status = AudioFileCreateWithURL((CFURLRef)tvarFilename, kAudioFileWAVEType, &audioFormat, kAudioFileFlags_EraseFile, &mRecordFile);
int beatsToRecord = 4; //temporary
int bpm = 120;
double intervalInSamples = (double) 60 / bpm;
intervalInSamples *= (double)44100;
int inNumberFrames = (intervalInSamples * beatsToRecord);
UInt32 frameBuffer[inNumberFrames];
int sampleTime = 0;
UInt32 thisSubBuffer[inNumberFrames];
for (int i = 0; i < inNumberFrames; i++) { frameBuffer[i] = 0; }
UInt32 bytesToWrite = inNumberFrames * sizeof(UInt32);
status = AudioFileWriteBytes(mRecordFile, false, 0, &bytesToWrite, &frameBuffer);
A WAV file is really simple: it only consists of a (usually 44-byte long) header section, then the raw PCM data. I've written a library which needs to record WAV files, and I'm pretty sure you'll understand how I accomplish it. For clarifying:
/**
* CD quality: 44100 Hz sample rate, 16 bit per sample, 2 channels (stereo):
**/
struct sprec_wav_header *hdr = sprec_wav_header_from_params(44100, 16, 2);
int filesize = (obtain the filesize somehow here);
/**
* -8 bytes for the first part of the header, see the WAV specification
**/
hdr->filesize = filesize - 8;
int filedesc = open("/tmp/dummy.wav", O_WRONLY | O_CREAT, 0644);
if (sprec_wav_header_write(filedesc, hdr))
{
printf("Error writing WAV header!\n");
}
close(filedesc);
free(hdr);
And the library I've written: https://github.com/H2CO3/libsprec/
Hope this helps.
The problem in code in initial question was in missing AudioFileClose(mRecordFile); line in the very end.
For those who still search for working sample without using 3rd party libraries, here is slightly modified code snippet:
- (void)createSilentWAVFileAtURL:(NSURL *)fileURL {
AudioFileID mRecordFile;
AudioStreamBasicDescription audioFormat;
audioFormat.mSampleRate = 44100.00;
audioFormat.mFormatID = kAudioFormatLinearPCM;
audioFormat.mFormatFlags = kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked;
audioFormat.mFramesPerPacket = 1;
audioFormat.mChannelsPerFrame = 2;
audioFormat.mBitsPerChannel = 16;
audioFormat.mBytesPerPacket = 4;
audioFormat.mBytesPerFrame = 4;
OSStatus status = AudioFileCreateWithURL((__bridge CFURLRef)fileURL, kAudioFileWAVEType, &audioFormat, kAudioFileFlags_EraseFile, &mRecordFile);
double intervalInSamples = 0.5;
intervalInSamples *= audioFormat.mSampleRate * audioFormat.mChannelsPerFrame;
int beatsToRecord = 4; //seconds of silence
int inNumberFrames = (intervalInSamples * beatsToRecord);
UInt32 frameBuffer[inNumberFrames];
for (int i = 0; i < inNumberFrames; i++) { frameBuffer[i] = 0; }
UInt32 bytesToWrite = inNumberFrames * sizeof(uint32_t);
status = AudioFileWriteBytes(mRecordFile, false, 0, &bytesToWrite, &frameBuffer);
status = AudioFileClose(mRecordFile);
NSAssert(status == noErr, #"");
}
P.S.: To decrease final file size reduce mChannelsPerFrame from 2 to 1 and mSampleRate (e.g. to 11000)