gpt4 book ai didi

c++ - iOS OpenAL Sound 不是定位的

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:50:21 26 4
gpt4 key购买 nike

好的,这是我的问题。

我正在研究一些帮助程序类,它们为我封装了 iOS 上的 OpenAL 功能。没错,它运行良好,这意味着我可以(同时)播放多种声音并在那里配置增益和音调。

我的下一步是添加位置声音。我知道,我必须为听者的位置和声源的位置使用属性(和速度,但这不是很重要,你知道吗)。

问题是,我按照网上的教程都实现了,但是声音总是没有衰减,所以不是定位的。

我发现,有时这是由立体声音频文件引起的,所以现在我使用单声道文件,但问题仍然存在。事实上,我下载了这个 example project来自苹果。它适用于 Mac OS,并且可以正常工作。所以我从他们的项目中获取了声音文件并在我自己的项目中使用它们。所以声音格式应该没有问题。它仍然没有回放位置。

所以我的问题是:是否可以在 iPhone 上使用 OpenAl 播放声音定位?如果是,要实现3D 播放,至少应该设置哪些OpenAL 属性?也许我只是没有设置所需的一项基本设置。

此外,我认为模拟器是问题所在,但当我在真正的 iPhone 上测试我的应用程序时,它也不起作用。

我会知道在这里添加我当前的代码,但我认为直接找到错误太过分了。我希望有人知道我烦人的问题的标准解决方案(我认为这会是一些小错误 :D )

代码如下:

听众:

class Listener {
private:
vec3 position;
vec3 velocity;
vec3 at;
vec3 up;
public:
Listener();

void setOrientation(vec3 at, vec3 up);

vec3 getPosition();
void setPosition(vec3 position);

vec3 getVelocity();
void setVelocity(vec3 velocity);
};

Listener::Listener() {
alListenerf(AL_GAIN, 0.5f);
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
}

void Listener::setOrientation(vec3 at, vec3 up)
{
printf("Listener Set Orientation\n");
ALfloat *orientation = (ALfloat*)malloc(6*sizeof(ALfloat));
orientation[0] = at.x;
orientation[1] = at.y;
orientation[2] = at.z;
orientation[3] = up.x;
orientation[4] = up.y;
orientation[5] = up.z;
alListenerfv(AL_ORIENTATION, orientation);
this->at = at;
this->up = up;
free(orientation);
}

vec3 Listener::getPosition()
{
return position;
}

void Listener::setPosition(vec3 position)
{
printf("Listener Set Position\n");
ALfloat *positionArray = (ALfloat*)malloc(3*sizeof(ALfloat));
positionArray[0] = position.x;
positionArray[1] = position.y;
positionArray[2] = position.z;
alListenerfv(AL_POSITION, positionArray);
this->position = position;
free(positionArray);
}

vec3 Listener::getVelocity()
{
return velocity;
}

void Listener::setVelocity(vec3 velocity)
{
printf("Listener Set Velocity\n");
ALfloat *velArray = (ALfloat*)malloc(3*sizeof(ALfloat));
velArray[0] = velocity.x;
velArray[1] = velocity.y;
velArray[2] = velocity.z;
alListenerfv(AL_VELOCITY, velArray);
this->velocity = velocity;
free(velArray);
}

示例

class Sample {
private:
UInt32 usageCount; // Number of Screens using this sound
const char *filename; // The Filename, should be unique!
ALuint audioBuffer; // The sound buffer in which it is stored
public:
Sample(const char* filename);
Sample(std::string);

void load();
bool unload();

std::string getFilename();
ALuint getAudioBuffer();
};

Sample::Sample(const char* filename)
{
usageCount = 0;
this->filename = filename;
}

Sample::Sample(std::string filename)
{
usageCount = 0;
this->filename = filename.c_str();
}

void Sample::load()
{
ALenum format;
ALvoid* data;
ALsizei size;
ALsizei freq;

if (usageCount == 0) {
data = SampleLoader::GetOpenALAudioData(filename, &size, &format, &freq);

// Generate Buffer
alGenBuffers(1, &audioBuffer);

// Fill Buffer With Data
alBufferData(audioBuffer, format, data, size, freq);

// Free Audio Data
if (data)
{
free(data);
data = NULL;
}
}

usageCount++;
}

// returns true, if usageCount got zero
bool Sample::unload()
{
if (usageCount == 1) {
// Delete Buffer
alDeleteBuffers(1, &audioBuffer);
return true;
}

usageCount--;
return false;
}

std::string Sample::getFilename()
{
std::string strFilename = std::string(filename);
return strFilename;
}

ALuint Sample::getAudioBuffer()
{
return audioBuffer;
}

来源

class Source {
vec3 position;
vec3 velocity;
vec3 direction;
ALfloat gain;
ALfloat pitch;
ALuint sourceID;
Sample *sample;
public:
void load();
void unload();

ALboolean isPlaying();

void play();
void repeat();
void stop();

void updateSample(Sample *sample);
void updatePosition(vec3 position);
void updateVelocity(vec3 velocity);
void updateDirection(vec3 direcation);
void updateOrienation(vec3 position, vec3 velocity, vec3 direction);
void updateGain(ALfloat gain);
void updatePitch(ALfloat pitch);
};

void Source::load()
{
alGenSources(1, &sourceID);

position = vec3(0.0f, 0.0f, 0.0f);
velocity = vec3(0.0f, 0.0f, 0.0f);
direction = vec3(0.0f, 0.0f, 0.0f);

alSourcefv(sourceID, AL_POSITION, value_ptr(position));
alSourcefv(sourceID, AL_VELOCITY, value_ptr(velocity));
alSourcefv(sourceID, AL_DIRECTION, value_ptr(direction));

gain = 0.5f;
pitch = 1.0f;

alSourcef(sourceID, AL_GAIN, gain);
alSourcef(sourceID, AL_PITCH, pitch);
}

void Source::unload()
{
alDeleteSources(1, &sourceID);
}

ALboolean Source::isPlaying()
{
ALint sourceState;
alGetSourcei(sourceID, AL_SOURCE_STATE, &sourceState);
return (sourceState == AL_PLAYING);
}

void Source::play()
{
alSourcei(sourceID, AL_LOOPING, AL_FALSE);
alSourcePlay(sourceID);
}

void Source::repeat()
{
alSourcei(sourceID, AL_LOOPING, AL_TRUE);
alSourcePlay(sourceID);
}

void Source::stop()
{
alSourceStop(sourceID);
}

void Source::updateSample(Sample *sample)
{
alSourcei(sourceID, AL_BUFFER, sample->getAudioBuffer());
}

void Source::updatePosition(vec3 position)
{
alSource3f(sourceID, AL_POSITION, position.x, position.y, position.z);
}

void Source::updateVelocity(vec3 velocity)
{
alSourcefv(sourceID, AL_VELOCITY, value_ptr(velocity));
}

void Source::updateDirection(vec3 direction)
{
alSourcefv(sourceID, AL_DIRECTION, value_ptr(direction));
}

void Source::updateOrienation(vec3 position, vec3 velocity, vec3 direction)
{
alSourcefv(sourceID, AL_POSITION, value_ptr(position));
alSourcefv(sourceID, AL_VELOCITY, value_ptr(velocity));
alSourcefv(sourceID, AL_DIRECTION, value_ptr(direction));
}

void Source::updateGain(ALfloat gain)
{
alSourcef(sourceID, AL_GAIN, gain);
}

void Source::updatePitch(ALfloat pitch)
{
alSourcef(sourceID, AL_PITCH, pitch);
}

示例加载器函数,取自苹果项目

void* SampleLoader::GetOpenALAudioData(const char* filename, ALsizei *outDataSize, ALenum *outDataFormat, ALsizei*  outSampleRate)
{
OSStatus err = noErr;
SInt64 theFileLengthInFrames = 0;
AudioStreamBasicDescription theFileFormat;
UInt32 thePropertySize = sizeof(theFileFormat);
ExtAudioFileRef extRef = NULL;
void* theData = NULL;
AudioStreamBasicDescription theOutputFormat;

// Create Path
NSString *filenameString = [NSString stringWithCString:filename encoding:NSUTF8StringEncoding];
NSArray *components = [filenameString componentsSeparatedByString:@"."];

NSString *audioFilePath = [[NSBundle mainBundle] pathForResource:[components objectAtIndex:0] ofType:[components objectAtIndex:1]];
NSURL *audioFileURL = [NSURL fileURLWithPath:audioFilePath];
CFURLRef inFileURL = (__bridge CFURLRef)audioFileURL;

// Open a file with ExtAudioFileOpen()
err = ExtAudioFileOpenURL(inFileURL, &extRef);
if(err) {
printf("MyGetOpenALAudioData: ExtAudioFileOpenURL FAILED, Error = %d\n", (int)err);

// Dispose the ExtAudioFileRef, it is no longer needed
if (extRef) ExtAudioFileDispose(extRef);
return theData;
}

// Get the audio data format
err = ExtAudioFileGetProperty(extRef, kExtAudioFileProperty_FileDataFormat, &thePropertySize, &theFileFormat);
if(err) {
printf("MyGetOpenALAudioData: ExtAudioFileGetProperty(kExtAudioFileProperty_FileDataFormat) FAILED, Error = %d\n", (int)err);

// Dispose the ExtAudioFileRef, it is no longer needed
if (extRef) ExtAudioFileDispose(extRef);
return theData;
}
if (theFileFormat.mChannelsPerFrame > 2) {
printf("MyGetOpenALAudioData - Unsupported Format, channel count is greater than stereo\n");

// Dispose the ExtAudioFileRef, it is no longer needed
if (extRef) ExtAudioFileDispose(extRef);
return theData;
}

// Set the client format to 16 bit signed integer (native-endian) data
// Maintain the channel count and sample rate of the original source format
theOutputFormat.mSampleRate = theFileFormat.mSampleRate;
theOutputFormat.mChannelsPerFrame = theFileFormat.mChannelsPerFrame;

theOutputFormat.mFormatID = kAudioFormatLinearPCM;
theOutputFormat.mBytesPerPacket = 2 * theOutputFormat.mChannelsPerFrame;
theOutputFormat.mFramesPerPacket = 1;
theOutputFormat.mBytesPerFrame = 2 * theOutputFormat.mChannelsPerFrame;
theOutputFormat.mBitsPerChannel = 16;
theOutputFormat.mFormatFlags = kAudioFormatFlagsNativeEndian | kAudioFormatFlagIsPacked | kAudioFormatFlagIsSignedInteger;

// Set the desired client (output) data format
err = ExtAudioFileSetProperty(extRef, kExtAudioFileProperty_ClientDataFormat, sizeof(theOutputFormat), &theOutputFormat);
if(err) {
printf("MyGetOpenALAudioData: ExtAudioFileSetProperty(kExtAudioFileProperty_ClientDataFormat) FAILED, Error = %d\n", (int)err);

// Dispose the ExtAudioFileRef, it is no longer needed
if (extRef) ExtAudioFileDispose(extRef);
return theData;
}

// Get the total frame count
thePropertySize = sizeof(theFileLengthInFrames);
err = ExtAudioFileGetProperty(extRef, kExtAudioFileProperty_FileLengthFrames, &thePropertySize, &theFileLengthInFrames);
if(err) {
printf("MyGetOpenALAudioData: ExtAudioFileGetProperty(kExtAudioFileProperty_FileLengthFrames) FAILED, Error = %d\n", (int)err);

// Dispose the ExtAudioFileRef, it is no longer needed
if (extRef) ExtAudioFileDispose(extRef);
return theData;
}

// Read all the data into memory
UInt32 theFramesToRead = (UInt32)theFileLengthInFrames;
UInt32 dataSize = theFramesToRead * theOutputFormat.mBytesPerFrame;;
theData = malloc(dataSize);
if (theData)
{
AudioBufferList theDataBuffer;
theDataBuffer.mNumberBuffers = 1;
theDataBuffer.mBuffers[0].mDataByteSize = dataSize;
theDataBuffer.mBuffers[0].mNumberChannels = theOutputFormat.mChannelsPerFrame;
theDataBuffer.mBuffers[0].mData = theData;

// Read the data into an AudioBufferList
err = ExtAudioFileRead(extRef, &theFramesToRead, &theDataBuffer);
if(err == noErr)
{
// success
*outDataSize = (ALsizei)dataSize;
*outDataFormat = (theOutputFormat.mChannelsPerFrame > 1) ? AL_FORMAT_STEREO16 : AL_FORMAT_MONO16;
*outSampleRate = (ALsizei)theOutputFormat.mSampleRate;
}
else
{
// failure
free (theData);
theData = NULL; // make sure to return NULL
printf("MyGetOpenALAudioData: ExtAudioFileRead FAILED, Error = %d\n", (int)err);

// Dispose the ExtAudioFileRef, it is no longer needed
if (extRef) ExtAudioFileDispose(extRef);
return theData;
}
}

// Dispose the ExtAudioFileRef, it is no longer needed
if (extRef) ExtAudioFileDispose(extRef);
return theData;
}

声音管理器

namespace SoundManager {
extern ALCdevice *openALDevice;
extern ALCcontext *openALContext;

extern std::vector<Sample*> samples;

void initialize();
void release();

Sample* manage(const char* filename);
Sample* manage(std::string filename);
void remove(Sample *sample);
}

namespace SoundManager {
ALCdevice *openALDevice;
ALCcontext *openALContext;

std::vector<Sample*> samples;
}

void AudioInterruptionListenerCallback(void* user_data, UInt32 interruption_state)
{
if (kAudioSessionBeginInterruption == interruption_state)
{
alcMakeContextCurrent(NULL);
}
else if (kAudioSessionEndInterruption == interruption_state)
{
AudioSessionSetActive(true);
alcMakeContextCurrent(SoundManager::openALContext);
}
}

void SoundManager::initialize()
{
AudioSessionInitialize(NULL, NULL, AudioInterruptionListenerCallback, NULL);

UInt32 session_category = kAudioSessionCategory_MediaPlayback;
AudioSessionSetProperty(kAudioSessionProperty_AudioCategory, sizeof(session_category), &session_category);

AudioSessionSetActive(true);

// Open Device
openALDevice = alcOpenDevice(NULL);

// Create and activate context
openALContext = alcCreateContext(openALDevice, NULL);
alcMakeContextCurrent(openALContext);
}

void SoundManager::release()
{
// Give up Context and destroy it
alcMakeContextCurrent(NULL);
alcDestroyContext(openALContext);

// Close device
alcCloseDevice(openALDevice);
}

Sample* SoundManager::manage(const char* filename)
{
return manage(std::string(filename));
}

Sample* SoundManager::manage(std::string filename)
{
if (samples.size() >= kMaxNumberOfSamples) {
Engine::warning("Max number of samples reached! Release one first");
return NULL;
}

// If Sound is already managed, load the managed again
for (int i=0; i<samples.size(); i++) {
if (samples[i]->getFilename().compare(filename) == 0) {
printf("Already loaded\n");
samples[i]->load();
return samples[i];
}
}

// Otherwise load the new sound and manage it
printf("Load new sound\n");
Sample *sample = new Sample(filename);
sample->load();
samples.push_back(sample);
return sample;
}

void SoundManager::remove(Sample *sample)
{
for (int i=0; i<samples.size(); i++) {
if (samples[i]->getFilename().compare(sample->getFilename()) == 0) {
// If Sound isn't in use from anywhere else, unmanage it
if (samples[i]->unload()) {
samples.erase(std::remove(samples.begin(), samples.end(), sample), samples.end());
return;
}
}
}

Engine::warning("The sound to be removed wasn't managed!");
}

所以就在这里,这是 Source、Buffer 和 Listener 的所有包装类,以及一个管理器,它只是确保一个 Sample 只加载一次。所有这些都应用在屏幕类中,该类在我的简单引擎中呈现。它是独立的,应该只播放从左到右再向后移动的声音。但事实并非如此:

class SoundTestScreen : public Screen
{
private:
void initialize();
void load();
void unload();

void update(Time &time);
void draw(Time &time);

private:
Listener listener;
Sample *bubbles;
Source *bubbleSource;

double totalTime;
double lastPlayTime;
};

void SoundTestScreen::initialize()
{
listener.setPosition(vec3(0, 0, 0));
listener.setVelocity(vec3(0, 0, 0));
listener.setOrientation(vec3(0, 0, -1),vec3(0, 1, 0));

totalTime = 0;
lastPlayTime = 0;

// Create Sources to play the samples
bubbleSource = new Source();
}

void SoundTestScreen::load()
{
// Only load sample once
bubbles = SoundManager::manage("sound_electric.wav");

bubbleSource->load();
bubbleSource->updateSample(bubbles);
bubbleSource->updatePosition(vec3(0,0,0));
bubbleSource->repeat();
}

void SoundTestScreen::unload()
{
// Only unload sample once
SoundManager::remove(bubbles);

bubbleSource->unload();
}

void SoundTestScreen::update(Time &time)
{
bubbleSource->updatePosition(vec3(100*sinf(time.ElapsedTime),0,0));
}

void SoundTestScreen::draw(Time &time)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
}

这只是一些基本的实现,但我真的不知道为什么声音不是定位的。

我真的希望有人能发现我的错误,这很烦人。

问候


编辑:

它现在可以工作了,我现在只纠正了一些小错误,但很长时间没有找到。我会把这段代码留给那些正在为 OpenAL 搜索 iOS 声音系统示例的人,据我所知,这在互联网上很少见。

最佳答案

哇,在给出示例代码时,你打下了雷声! :D

所以我从中学到的位置代码是这样的:

#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <AL\al.h>
#include <AL\alc.h>
#include <AL\alut.h>

// Maximum data buffers we will need.
#define NUM_BUFFERS 3

// Maximum emissions we will need.
#define NUM_SOURCES 3

// These index the buffers and sources.
#define BATTLE 0
#define GUN1 1
#define GUN2 2

// Buffers hold sound data.
ALuint Buffers[NUM_BUFFERS];

// Sources are points of emitting sound.
ALuint Sources[NUM_SOURCES];

// Position of the source sounds.
ALfloat SourcesPos[NUM_SOURCES][3];

// Velocity of the source sounds.
ALfloat SourcesVel[NUM_SOURCES][3];


// Position of the listener.
ALfloat ListenerPos[] = { 0.0, 0.0, 0.0 };

// Velocity of the listener.
ALfloat ListenerVel[] = { 0.0, 0.0, 0.0 };

// Orientation of the listener. (first 3 elements are "at", second 3 are "up")
ALfloat ListenerOri[] = { 0.0, 0.0, -1.0, 0.0, 1.0, 0.0 };


/*
* ALboolean LoadALData()
*
* This function will load our sample data from the disk using the Alut
* utility and send the data into OpenAL as a buffer. A source is then
* also created to play that buffer.
*/
ALboolean LoadALData()
{

alutInit (NULL, NULL);

//ALUT allows us to point to a buffer object using an ALuint.
// furthermore, there are calls that allow you to load from memory.
Buffers[BATTLE] = alutCreateBufferFromFile("C:\\Users\\Lee\\Desktop\\OpenAL\\OpenALStaticSound\\wavdata\\Battle.wav");
Buffers[GUN1] = alutCreateBufferFromFile("C:\\Users\\Lee\\Desktop\\OpenAL\\OpenALStaticSound\\wavdata\\Gun1.wav");
Buffers[GUN2] = alutCreateBufferFromFile("C:\\Users\\Lee\\Desktop\\OpenAL\\OpenALStaticSound\\wavdata\\Gun2.wav");

// Bind the buffer with the source.
//alGenSources (1, &Source);
alGenSources(NUM_SOURCES, Sources);

//Attach any special modifications to the source
//alSourcei (Source, AL_BUFFER, Buffer );
//alSourcef (Source, AL_PITCH, 1.0 );
//alSourcef (Source, AL_GAIN, 1.0 );
//alSourcefv(Source, AL_POSITION, SourcePos);
//alSourcefv(Source, AL_VELOCITY, SourceVel);
//alSourcei (Source, AL_LOOPING, AL_TRUE );

alSourcei (Sources[BATTLE], AL_BUFFER, Buffers[BATTLE] );
alSourcef (Sources[BATTLE], AL_PITCH, 1.0 );
alSourcef (Sources[BATTLE], AL_GAIN, 1.0 );
alSourcefv(Sources[BATTLE], AL_POSITION, SourcesPos[BATTLE]);
alSourcefv(Sources[BATTLE], AL_VELOCITY, SourcesVel[BATTLE]);
alSourcei (Sources[BATTLE], AL_LOOPING, AL_TRUE );

alSourcei (Sources[GUN1], AL_BUFFER, Buffers[GUN1] );
alSourcef (Sources[GUN1], AL_PITCH, 1.0 );
alSourcef (Sources[GUN1], AL_GAIN, 1.0 );
alSourcefv(Sources[GUN1], AL_POSITION, SourcesPos[GUN1]);
alSourcefv(Sources[GUN1], AL_VELOCITY, SourcesVel[GUN1]);
alSourcei (Sources[GUN1], AL_LOOPING, AL_FALSE );

alSourcei (Sources[GUN2], AL_BUFFER, Buffers[GUN2] );
alSourcef (Sources[GUN2], AL_PITCH, 1.0 );
alSourcef (Sources[GUN2], AL_GAIN, 1.0 );
alSourcefv(Sources[GUN2], AL_POSITION, SourcesPos[GUN2]);
alSourcefv(Sources[GUN2], AL_VELOCITY, SourcesVel[GUN2]);
alSourcei (Sources[GUN2], AL_LOOPING, AL_FALSE );



return AL_TRUE;
}

/*
* void SetListenerValues()
*
* We already defined certain values for the Listener, but we need
* to tell OpenAL to use that data. This function does just that.
*/
void SetListenerValues()
{
alListenerfv(AL_POSITION, ListenerPos);
alListenerfv(AL_VELOCITY, ListenerVel);
alListenerfv(AL_ORIENTATION, ListenerOri);
}

/*
* void KillALData()
*
* We have allocated memory for our buffers and sources which needs
* to be returned to the system. This function frees that memory.
*/
void KillALData()
{
alDeleteBuffers(NUM_BUFFERS, &Buffers[0]);
alDeleteSources(NUM_SOURCES, &Sources[0]);
alutExit();
}


int main(){


printf("Original Tutorial : MindCode's OpenAL Lesson 1: Single Static Source\n\n");
printf("Updated Tutorial : Lesley A. Gushurst\n");

alutInit(NULL, 0);
//alGetError();

// Load the wav data.

if(LoadALData() == AL_FALSE)
{
printf("Error loading data.");
return 0;
} else {
// Begin the battle sample to play.
alSourcePlay(Sources[BATTLE]);

// Go through all the sources and check that they are playing.
// Skip the first source because it is looping anyway (will always be playing).
ALint play;

while (!_kbhit())
{
for (int i = 1; i < NUM_SOURCES; i++)
{
alGetSourcei(Sources[i], AL_SOURCE_STATE, &play);

if (play != AL_PLAYING)
{
// Pick a random position around the listener to play the source.

double theta = (double) (rand() % 360) * 3.14 / 180.0;

SourcesPos[i][0] = -float(cos(theta));
SourcesPos[i][1] = -float(rand()%2);
SourcesPos[i][2] = -float(sin(theta));

alSourcefv(Sources[i], AL_POSITION, SourcesPos[i] );

alSourcePlay(Sources[i]);
}
}
}
getchar();
}

return 0;
}

我会尝试这段代码,如果它有效,那么您的问题可能是“是否可以在 iPhone 上使用 OpenAl 播放位置声音?”将得到答复。我不确定,因为我没有做过移动开发,但我认为这是一个很好的起点。非常尊重 MindCode。你必须用其他东西替换 wav 文件来测试它(而不是像我那样硬编码)。

关于c++ - iOS OpenAL Sound 不是定位的,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30193190/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com