Skip to content

Commit

Permalink
0.17.0-rc4: finally actually properly fix song playback
Browse files Browse the repository at this point in the history
now we use an atomic integer as a generation counter which solves
the problem, and conveniently also solves it in a thread-safe way.
  • Loading branch information
zhiayang committed Mar 6, 2021
1 parent f42bedc commit a688188
Show file tree
Hide file tree
Showing 5 changed files with 42 additions and 15 deletions.
10 changes: 7 additions & 3 deletions MoeStreamer.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
objects = {

/* Begin PBXBuildFile section */
2B009AC825F3389C001364F0 /* Atomic.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B009AC725F3389C001364F0 /* Atomic.swift */; };
2B125D7D253753D1000C45C5 /* MediaKeyHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B125D7C253753D1000C45C5 /* MediaKeyHandler.swift */; };
2B22659F25412DB600F970B9 /* ListenMoeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B22659E25412DB600F970B9 /* ListenMoeController.swift */; };
2B2D2C7D256E9F1700ED6DB5 /* RateLimiter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2B2D2C7C256E9F1700ED6DB5 /* RateLimiter.swift */; };
Expand Down Expand Up @@ -87,6 +88,7 @@
/* End PBXCopyFilesBuildPhase section */

/* Begin PBXFileReference section */
2B009AC725F3389C001364F0 /* Atomic.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Atomic.swift; sourceTree = "<group>"; };
2B125D7C253753D1000C45C5 /* MediaKeyHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaKeyHandler.swift; sourceTree = "<group>"; };
2B22659E25412DB600F970B9 /* ListenMoeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListenMoeController.swift; sourceTree = "<group>"; };
2B2D2C7C256E9F1700ED6DB5 /* RateLimiter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RateLimiter.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -222,6 +224,7 @@
2B125D7C253753D1000C45C5 /* MediaKeyHandler.swift */,
2BECD3E82577784500FF2544 /* NotifCentreNowPlaying.swift */,
2BECD3EA2577852F00FF2544 /* InsomniaInducer.swift */,
2B009AC725F3389C001364F0 /* Atomic.swift */,
);
path = system;
sourceTree = "<group>";
Expand Down Expand Up @@ -400,7 +403,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 1130;
LastUpgradeCheck = 1200;
LastUpgradeCheck = 1240;
ORGANIZATIONNAME = zhiayang;
TargetAttributes = {
2BD486BD23EFE3030066F1C4 = {
Expand Down Expand Up @@ -472,6 +475,7 @@
2BE995F223FBAB340092023C /* PopupButton.swift in Sources */,
2B4077E8256D84400054F4DC /* MainModel.swift in Sources */,
2BE995F823FBEA4F0092023C /* LocalAudioController.swift in Sources */,
2B009AC825F3389C001364F0 /* Atomic.swift in Sources */,
2BE995F023FBA0CE0092023C /* AudioController.swift in Sources */,
2BD486D523EFE6C20066F1C4 /* ViewController.swift in Sources */,
2B125D7D253753D1000C45C5 /* MediaKeyHandler.swift in Sources */,
Expand Down Expand Up @@ -644,7 +648,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = "0.17.0-rc3";
MARKETING_VERSION = "0.17.0-rc4";
PRODUCT_BUNDLE_IDENTIFIER = com.zhiayang.MoeStreamer;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "";
Expand Down Expand Up @@ -678,7 +682,7 @@
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.15;
MARKETING_VERSION = "0.17.0-rc3";
MARKETING_VERSION = "0.17.0-rc4";
PRODUCT_BUNDLE_IDENTIFIER = com.zhiayang.MoeStreamer;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1240"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1200"
LastUpgradeVersion = "1240"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
Expand Down
23 changes: 13 additions & 10 deletions MoeStreamer/src/audio/AudioEngine.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ class AudioEngine
private var mirrorDevice: AudioDevice
private var savedElapsedTime: Double = 0

// super thread unsafe
private var didStopPlayer: Bool = false
private var songGeneration = Atomic64(0)

init()
{
Expand Down Expand Up @@ -58,31 +57,35 @@ class AudioEngine
return
}

// store the current gen first, so the closure can capture it,
// then increment it. incr() returns the old value.
let currentGen = self.songGeneration.incr()

// if the player was playing, then we should resume it after replacing the song.
let shouldResume = self.p1.isPlaying

// either way, we need to stop in order to clear the current queue
// of scheduled buffers/songs in the player.
self.p1.stop()
self.didStopPlayer = true

// apparently, when you stop the player, all the completion handlers for all the scheduled songs are
// called -- even if they never even got a chance to play. that would explain the repeated calls.
self.p1.scheduleFile(file, at: nil, completionCallbackType: .dataPlayedBack, completionHandler: { [weak self] _ in
guard let self = self else {
return
}

if self.didStopPlayer {
self.didStopPlayer = false
// if and only if the captured generation is exactly 1 behind the current generation, then we are responsible
// for controlling the playback and/or calling the next completion handler. if not, then we quit.
guard let self = self, currentGen + 1 == self.songGeneration.value else {
return
}

// since onComplete probably ends up calling this function itself, we have to do it from
// a separate thread, since apparently calling stop() from inside this handler causes a deadlock
DispatchQueue.main.async {
// since onComplete probably ends up calling this function itself, we have to do it from
// a separate thread, since apparently calling stop() from inside this handler causes a deadlock
onComplete()
}
})

// if it was playing before, then resume playing.
if shouldResume {
self.p1.play()
}
Expand Down
20 changes: 20 additions & 0 deletions MoeStreamer/src/system/Atomic.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Atomic.swift
// Copyright (c) 2021, zhiayang
// Licensed under the Apache License Version 2.0.

import Foundation

struct Atomic64
{
private(set) var value: Int64

init(_ initial: Int64)
{
self.value = initial
}

// the OSAtomic functions return the new value; to get the old value we just do an
// offset, which is perfectly safe since we know for sure the incr/decr already happened.
mutating func incr() -> Int64 { return OSAtomicIncrement64(&self.value) - 1 }
mutating func decr() -> Int64 { return OSAtomicDecrement64(&self.value) + 1 }
}

0 comments on commit a688188

Please sign in to comment.