Cog Audio: Now preserves already resampled output when switching output formats
parent
ee509b6e13
commit
92d29e7acf
|
@ -15,6 +15,7 @@
|
|||
#import <audio/audio_resampler.h>
|
||||
|
||||
#import "Node.h"
|
||||
#import "RefillNode.h"
|
||||
|
||||
@interface ConverterNode : Node {
|
||||
NSDictionary * rgInfo;
|
||||
|
@ -49,6 +50,11 @@
|
|||
AudioStreamBasicDescription floatFormat;
|
||||
AudioStreamBasicDescription dmFloatFormat; // downmixed/upmixed float format
|
||||
AudioStreamBasicDescription outputFormat;
|
||||
|
||||
AudioStreamBasicDescription previousOutputFormat;
|
||||
AudioStreamBasicDescription rememberedInputFormat;
|
||||
RefillNode *refillNode;
|
||||
id __weak originalPreviousNode;
|
||||
}
|
||||
|
||||
- (id)initWithController:(id)c previous:(id)p;
|
||||
|
|
|
@ -59,6 +59,9 @@ void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
|
|||
latencyStarted = -1;
|
||||
latencyEaten = 0;
|
||||
latencyPostfill = NO;
|
||||
|
||||
refillNode = nil;
|
||||
originalPreviousNode = nil;
|
||||
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:nil];
|
||||
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.outputResampling" options:0 context:nil];
|
||||
|
@ -415,6 +418,17 @@ static void extrapolate(float *buffer, size_t channels, size_t frameSize, size_t
|
|||
usleep(500);
|
||||
continue;
|
||||
}
|
||||
else if (refillNode)
|
||||
{
|
||||
// refill node just ended, file resumes
|
||||
[self setPreviousNode:originalPreviousNode];
|
||||
[self setEndOfStream:NO];
|
||||
[self setShouldContinue:YES];
|
||||
refillNode = nil;
|
||||
[self cleanUp];
|
||||
[self setupWithInputFormat:rememberedInputFormat outputFormat:outputFormat];
|
||||
continue;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
[self writeData:writeBuf amount:amountConverted];
|
||||
|
@ -429,7 +443,6 @@ static void extrapolate(float *buffer, size_t channels, size_t frameSize, size_t
|
|||
int extrapolateStart = 0;
|
||||
int extrapolateEnd = 0;
|
||||
size_t amountToSkip = 0;
|
||||
BOOL endOfInputStream = NO;
|
||||
|
||||
if (stopping)
|
||||
return 0;
|
||||
|
@ -530,7 +543,12 @@ tryagain:
|
|||
size_t bytesRead = [self readData:inputBuffer + amountToSkip + bytesReadFromInput amount:(int)(amountToWrite - bytesReadFromInput)];
|
||||
bytesReadFromInput += bytesRead;
|
||||
if (!bytesRead)
|
||||
usleep(500);
|
||||
{
|
||||
if (refillNode)
|
||||
[self setEndOfStream:YES];
|
||||
else
|
||||
usleep(500);
|
||||
}
|
||||
}
|
||||
|
||||
bytesReadFromInput += amountToSkip;
|
||||
|
@ -612,7 +630,7 @@ tryagain:
|
|||
inpOffset = 0;
|
||||
}
|
||||
|
||||
if (floatOffset == floatSize)
|
||||
if (inpOffset != inpSize && floatOffset == floatSize)
|
||||
{
|
||||
struct resampler_data src_data;
|
||||
|
||||
|
@ -875,6 +893,7 @@ static float db_to_scale(float db)
|
|||
- (void)setOutputFormat:(AudioStreamBasicDescription)format
|
||||
{
|
||||
DLog(@"SETTING OUTPUT FORMAT!");
|
||||
previousOutputFormat = outputFormat;
|
||||
outputFormat = format;
|
||||
outputFormatChanged = YES;
|
||||
}
|
||||
|
@ -884,13 +903,34 @@ static float db_to_scale(float db)
|
|||
DLog(@"FORMAT CHANGED");
|
||||
paused = YES;
|
||||
[self cleanUp];
|
||||
if (outputFormatChanged)
|
||||
if (outputFormatChanged && ![buffer isEmpty])
|
||||
{
|
||||
[writeLock lock];
|
||||
[buffer empty];
|
||||
[writeLock unlock];
|
||||
// Transfer previously buffered data, remember input format
|
||||
rememberedInputFormat = format;
|
||||
originalPreviousNode = previousNode;
|
||||
refillNode = [[RefillNode alloc] initWithController:controller previous:nil];
|
||||
[self setPreviousNode:refillNode];
|
||||
|
||||
int dataRead = 0;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
void * ptr;
|
||||
dataRead = [buffer lengthAvailableToReadReturningPointer:&ptr];
|
||||
if (dataRead) {
|
||||
[refillNode writeData:(float*)ptr floatCount:dataRead / sizeof(float)];
|
||||
[buffer didReadLength:dataRead];
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
[self setupWithInputFormat:previousOutputFormat outputFormat:outputFormat];
|
||||
}
|
||||
else
|
||||
{
|
||||
[self setupWithInputFormat:format outputFormat:outputFormat];
|
||||
}
|
||||
[self setupWithInputFormat:format outputFormat:outputFormat];
|
||||
}
|
||||
|
||||
- (void)setRGInfo:(NSDictionary *)rgi
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
{
|
||||
format = *f;
|
||||
// Calculate a ratio and add to double(seconds) instead, as format may change
|
||||
double oldSampleRatio = sampleRatio;
|
||||
// double oldSampleRatio = sampleRatio;
|
||||
sampleRatio = 1.0 / (format.mSampleRate * format.mBytesPerPacket);
|
||||
BufferChain *bufferChain = [controller bufferChain];
|
||||
if (bufferChain)
|
||||
|
@ -102,8 +102,13 @@
|
|||
// This clears the resampler buffer, but not the input buffer
|
||||
// We also have to jump the play position ahead accounting for
|
||||
// the data we are flushing
|
||||
#if 0
|
||||
// We no longer need to do this, because outputchanged converter
|
||||
// now uses the RefillNode to slap the previous samples into
|
||||
// itself
|
||||
if (oldSampleRatio)
|
||||
amountPlayed += oldSampleRatio * [[converter buffer] bufferedLength];
|
||||
#endif
|
||||
[converter setOutputFormat:format];
|
||||
[converter inputFormatDidChange:[bufferChain inputFormat]];
|
||||
}
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
//
|
||||
// RefillNode.h
|
||||
// Cog
|
||||
//
|
||||
// Created by Christopher SNowhill on 1/13/22.
|
||||
// Copyright 2022 __LoSnoCo__. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import <CoreAudio/AudioHardware.h>
|
||||
#import <AudioToolbox/AudioToolbox.h>
|
||||
#import <AudioUnit/AudioUnit.h>
|
||||
|
||||
#import "Node.h"
|
||||
#import "Plugin.h"
|
||||
|
||||
#define INPUT_NODE_SEEK
|
||||
|
||||
@interface RefillNode : Node {
|
||||
// This node just slaps pre-converted data into its buffer for re-buffering
|
||||
}
|
||||
|
||||
- (void)writeData:(float *)data floatCount:(size_t)count;
|
||||
|
||||
@end
|
|
@ -0,0 +1,34 @@
|
|||
//
|
||||
// RefillNode.m
|
||||
// Cog
|
||||
//
|
||||
// Created by Christopher Snowhill on 1/13/22.
|
||||
// Copyright 2022 __LoSnoCo__. All rights reserved.
|
||||
//
|
||||
|
||||
#import "Plugin.h"
|
||||
#import "RefillNode.h"
|
||||
|
||||
#import "Logging.h"
|
||||
|
||||
@implementation RefillNode
|
||||
|
||||
- (id)initWithController:(id)c previous:(id)p {
|
||||
self = [super initWithController:c previous:p];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
- (void)writeData:(float *)data floatCount:(size_t)count
|
||||
{
|
||||
[self writeData:data amount:(int)(count * sizeof(float))];
|
||||
}
|
||||
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
DLog(@"Refill Node dealloc");
|
||||
}
|
||||
|
||||
@end
|
|
@ -87,6 +87,8 @@
|
|||
8389F2A4278E646E0074164C /* lpc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8389F2A2278E646E0074164C /* lpc.c */; };
|
||||
839366671815923C006DD712 /* CogPluginMulti.h in Headers */ = {isa = PBXBuildFile; fileRef = 839366651815923C006DD712 /* CogPluginMulti.h */; };
|
||||
839366681815923C006DD712 /* CogPluginMulti.m in Sources */ = {isa = PBXBuildFile; fileRef = 839366661815923C006DD712 /* CogPluginMulti.m */; };
|
||||
83A44A01279119B50049B6E2 /* RefillNode.m in Sources */ = {isa = PBXBuildFile; fileRef = 83A449FF279119B50049B6E2 /* RefillNode.m */; };
|
||||
83A44A02279119B50049B6E2 /* RefillNode.h in Headers */ = {isa = PBXBuildFile; fileRef = 83A44A00279119B50049B6E2 /* RefillNode.h */; };
|
||||
8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; };
|
||||
8E8D3D2F0CBAEE6E00135C1B /* AudioContainer.h in Headers */ = {isa = PBXBuildFile; fileRef = 8E8D3D2D0CBAEE6E00135C1B /* AudioContainer.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
8E8D3D300CBAEE6E00135C1B /* AudioContainer.m in Sources */ = {isa = PBXBuildFile; fileRef = 8E8D3D2E0CBAEE6E00135C1B /* AudioContainer.m */; };
|
||||
|
@ -203,6 +205,8 @@
|
|||
8389F2A2278E646E0074164C /* lpc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lpc.c; sourceTree = "<group>"; };
|
||||
839366651815923C006DD712 /* CogPluginMulti.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CogPluginMulti.h; sourceTree = "<group>"; };
|
||||
839366661815923C006DD712 /* CogPluginMulti.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CogPluginMulti.m; sourceTree = "<group>"; };
|
||||
83A449FF279119B50049B6E2 /* RefillNode.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RefillNode.m; sourceTree = "<group>"; };
|
||||
83A44A00279119B50049B6E2 /* RefillNode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RefillNode.h; sourceTree = "<group>"; };
|
||||
8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
8DC2EF5B0486A6940098B216 /* CogAudio.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CogAudio.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
8E8D3D2D0CBAEE6E00135C1B /* AudioContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioContainer.h; sourceTree = "<group>"; };
|
||||
|
@ -328,6 +332,8 @@
|
|||
17D21C750B8BE4BA00D1EBDE /* Chain */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83A44A00279119B50049B6E2 /* RefillNode.h */,
|
||||
83A449FF279119B50049B6E2 /* RefillNode.m */,
|
||||
17D21C760B8BE4BA00D1EBDE /* BufferChain.h */,
|
||||
17D21C770B8BE4BA00D1EBDE /* BufferChain.m */,
|
||||
8EC1225D0B993BD500C5B3AD /* ConverterNode.h */,
|
||||
|
@ -748,6 +754,7 @@
|
|||
8389F292278E64590074164C /* s16_to_float.h in Headers */,
|
||||
8389F279278E64590074164C /* utf.h in Headers */,
|
||||
8389F288278E64590074164C /* retro_environment.h in Headers */,
|
||||
83A44A02279119B50049B6E2 /* RefillNode.h in Headers */,
|
||||
8EC1225F0B993BD500C5B3AD /* ConverterNode.h in Headers */,
|
||||
8389F28C278E64590074164C /* file_stream.h in Headers */,
|
||||
8384912718080FF100E7332D /* Logging.h in Headers */,
|
||||
|
@ -850,6 +857,7 @@
|
|||
17C940240B900909008627D6 /* AudioMetadataReader.m in Sources */,
|
||||
17B619310B909BC300BC003F /* AudioPropertiesReader.m in Sources */,
|
||||
17ADB13D0B97926D00257CA2 /* AudioSource.m in Sources */,
|
||||
83A44A01279119B50049B6E2 /* RefillNode.m in Sources */,
|
||||
8EC122600B993BD500C5B3AD /* ConverterNode.m in Sources */,
|
||||
8E8D3D300CBAEE6E00135C1B /* AudioContainer.m in Sources */,
|
||||
B0575F300D687A4000411D77 /* Helper.m in Sources */,
|
||||
|
@ -998,9 +1006,7 @@
|
|||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||
GCC_NO_COMMON_BLOCKS = YES;
|
||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
||||
"HAVE_CONFIG_H=1",
|
||||
);
|
||||
GCC_PREPROCESSOR_DEFINITIONS = "HAVE_CONFIG_H=1";
|
||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||
GCC_WARN_ABOUT_RETURN_TYPE = YES;
|
||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
||||
|
|
Loading…
Reference in New Issue