Cog Audio: Now preserves already resampled output when switching output formats

CQTexperiment
Christopher Snowhill 2022-01-13 19:43:18 -08:00
parent ee509b6e13
commit 92d29e7acf
6 changed files with 129 additions and 12 deletions

View File

@ -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;

View File

@ -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

View File

@ -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]];
}

26
Audio/Chain/RefillNode.h Normal file
View File

@ -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

34
Audio/Chain/RefillNode.m Normal file
View File

@ -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

View File

@ -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;