Причина, по которой аудио останавливается, заключается в том, что у вас есть только один AVAudioPlayer, поэтому, когда вы попросите класс воспроизвести другой звук, вы в настоящее время заменяете старый экземпляр новым экземпляром AVAudioPlayer. Вы переписываете его в основном.
Вы можете создать два экземпляра класса GSAudio, а затем вызвать playSound для каждого из них или сделать класс универсальным аудио-менеджером, который использует словарь аудиопланеров.
I много предпочитает последний вариант, поскольку он позволяет использовать более чистый код и также более эффективен. Вы можете проверить, действительно ли вы уже сделали плеер для звука, вместо того, чтобы, например, создать нового игрока.
В любом случае я переделал ваш класс для вас, чтобы он воспроизводил несколько звуков однажды. Он также может воспроизводить один и тот же звук над собой (он не заменяет предыдущий экземпляр звука). Надеюсь, что это поможет!
Класс является одноэлементным, поэтому для доступа к классу используйте:
GSAudio.sharedInstance
, например, для воспроизведения звука, который вы вызывали:
GSAudio.sharedInstance.playSound("AudioFileName")
, и одновременно воспроизводить несколько звуков:
GSAudio.sharedInstance.playSounds("AudioFileName1", "AudioFileName2")
или вы могли бы загрузите звуки в массиве где-нибудь и вызовите функцию playSounds, которая принимает массив:
let sounds = ["AudioFileName1", "AudioFileName2"]
GSAudio.sharedInstance.playSounds(sounds)
Я также добавил функцию playSounds, которая позволяет вам откладывать каждый звук, воспроизводимый в каскадном формате. Итак:
let soundFileNames = ["SoundFileName1", "SoundFileName2", "SoundFileName3"]
GSAudio.sharedInstance.playSounds(soundFileNames, withDelay: 1.0)
будет воспроизводить звук2 через секунду после звука1, затем звук3 будет воспроизводить секунду после звука2 и т. Д.
Вот класс:
class GSAudio: NSObject, AVAudioPlayerDelegate {
static let sharedInstance = GSAudio()
private override init() {}
var players = [NSURL:AVAudioPlayer]()
var duplicatePlayers = [AVAudioPlayer]()
func playSound (soundFileName: String){
let soundFileNameURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource(soundFileName, ofType: "aif", inDirectory:"Sounds")!)
if let player = players[soundFileNameURL] { //player for sound has been found
if player.playing == false { //player is not in use, so use that one
player.prepareToPlay()
player.play()
} else { // player is in use, create a new, duplicate, player and use that instead
let duplicatePlayer = try! AVAudioPlayer(contentsOfURL: soundFileNameURL)
//use 'try!' because we know the URL worked before.
duplicatePlayer.delegate = self
//assign delegate for duplicatePlayer so delegate can remove the duplicate once it's stopped playing
duplicatePlayers.append(duplicatePlayer)
//add duplicate to array so it doesn't get removed from memory before finishing
duplicatePlayer.prepareToPlay()
duplicatePlayer.play()
}
} else { //player has not been found, create a new player with the URL if possible
do{
let player = try AVAudioPlayer(contentsOfURL: soundFileNameURL)
players[soundFileNameURL] = player
player.prepareToPlay()
player.play()
} catch {
print("Could not play sound file!")
}
}
}
func playSounds(soundFileNames: [String]){
for soundFileName in soundFileNames {
playSound(soundFileName)
}
}
func playSounds(soundFileNames: String...){
for soundFileName in soundFileNames {
playSound(soundFileName)
}
}
func playSounds(soundFileNames: [String], withDelay: Double) { //withDelay is in seconds
for (index, soundFileName) in soundFileNames.enumerate() {
let delay = withDelay*Double(index)
let _ = NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: #selector(playSoundNotification(_:)), userInfo: ["fileName":soundFileName], repeats: false)
}
}
func playSoundNotification(notification: NSNotification) {
if let soundFileName = notification.userInfo?["fileName"] as? String {
playSound(soundFileName)
}
}
func audioPlayerDidFinishPlaying(player: AVAudioPlayer, successfully flag: Bool) {
duplicatePlayers.removeAtIndex(duplicatePlayers.indexOf(player)!)
//Remove the duplicate player once it is done
}
}
ответ Nick's является хорошей начальной точкой, но неполный, поскольку на самом деле необходимо перегрузиться:
private:
void* operator new(size_t); // standard new
void* operator new(size_t, void*); // placement new
void* operator new[](size_t); // array new
void* operator new[](size_t, void*); // placement array new
(Хорошая практика кодирования предложила бы, необходимо также перегрузить удаление и удалить [] операторы - я был бы, но так как они не собираются называться, это не действительно необходимо.)
Pauldoo также корректен, что это не переживает агрегацию на Foo, хотя это действительно переживает наследование от Foo. Вы могли сделать некоторое шаблонное волшебство метапрограммирования ПОМОЧЬ предотвратить это, но это не будет неуязвимо для "злых пользователей" и таким образом вероятно, не стоит сложности. Документация того, как это должно использоваться, и обзор кода для обеспечения его, используется правильно, путь на только ~100%.
Я не знаю, как сделать это надежно и портативным способом.. но..
, Если объект находится на стеке тогда, Вы могли бы быть в состоянии утверждать в конструкторе, что значение 'этого' всегда близко к указателю вершины стека. Существует хороший шанс, что объект будет на стеке, если это верно.
я полагаю, что не все платформы реализуют свои стеки в том же направлении, таким образом, Вы могли бы хотеть сделать одноразовый тест, когда приложение начинает проверять, какой путь стек выращивает.. Или сделайте некоторую выдумку:
FooClass::FooClass() {
char dummy;
ptrdiff_t displacement = &dummy - reinterpret_cast<char*>(this);
if (displacement > 10000 || displacement < -10000) {
throw "Not on the stack - maybe..";
}
}
Вы могли перегрузить новый для Foo и сделать его частным. Это означало бы, что компилятор будет стонать..., если Вы не создадите экземпляр Foo на "куче" из Foo. Для ловли этого случая Вы не могли просто записать новый метод Foo, и затем компоновщик будет стонать о неопределенных символах.
class Foo {
private:
void* operator new(size_t size);
};
пз. Да, я знаю, что это может обойтись легко. Я действительно не рекомендую его - я думаю, что это - плохая идея - я просто отвечал на вопрос!;-)
@Nick
Это могло обойтись путем создания класса, который происходит из или агрегировал Foo. Я думаю, что я предлагаю (в то время как не устойчивый), все еще работал бы на полученные и агрегирующиеся классы.
, Например:
struct MyStruct {
Foo m_foo;
};
MyStruct* p = new MyStruct();
Здесь я создал экземпляр 'Foo' на "куче", обойдя скрытый новый оператор Foo.
Вы могли объявить функцию, вызванную "оператор, новый" в классе Foo, который блокирует доступ к нормальной форме новых.
действительно ли это - вид поведения, которое Вы хотите?
Вы могли объявить его как интерфейс и управлять классом реализации более непосредственно от Вашего собственного кода.
Не уверенный, если это предлагает какие-либо возможности времени компиляции, но Вы посмотрели на перегрузку 'нового' оператора для Вашего класса?