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 <audio/audio_resampler.h>
#import "Node.h" #import "Node.h"
#import "RefillNode.h"
@interface ConverterNode : Node { @interface ConverterNode : Node {
NSDictionary * rgInfo; NSDictionary * rgInfo;
@ -49,6 +50,11 @@
AudioStreamBasicDescription floatFormat; AudioStreamBasicDescription floatFormat;
AudioStreamBasicDescription dmFloatFormat; // downmixed/upmixed float format AudioStreamBasicDescription dmFloatFormat; // downmixed/upmixed float format
AudioStreamBasicDescription outputFormat; AudioStreamBasicDescription outputFormat;
AudioStreamBasicDescription previousOutputFormat;
AudioStreamBasicDescription rememberedInputFormat;
RefillNode *refillNode;
id __weak originalPreviousNode;
} }
- (id)initWithController:(id)c previous:(id)p; - (id)initWithController:(id)c previous:(id)p;

View File

@ -59,6 +59,9 @@ void PrintStreamDesc (AudioStreamBasicDescription *inDesc)
latencyStarted = -1; latencyStarted = -1;
latencyEaten = 0; latencyEaten = 0;
latencyPostfill = NO; latencyPostfill = NO;
refillNode = nil;
originalPreviousNode = nil;
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:nil]; [[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.volumeScaling" options:0 context:nil];
[[NSUserDefaultsController sharedUserDefaultsController] addObserver:self forKeyPath:@"values.outputResampling" 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); usleep(500);
continue; 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; else break;
} }
[self writeData:writeBuf amount:amountConverted]; [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 extrapolateStart = 0;
int extrapolateEnd = 0; int extrapolateEnd = 0;
size_t amountToSkip = 0; size_t amountToSkip = 0;
BOOL endOfInputStream = NO;
if (stopping) if (stopping)
return 0; return 0;
@ -530,7 +543,12 @@ tryagain:
size_t bytesRead = [self readData:inputBuffer + amountToSkip + bytesReadFromInput amount:(int)(amountToWrite - bytesReadFromInput)]; size_t bytesRead = [self readData:inputBuffer + amountToSkip + bytesReadFromInput amount:(int)(amountToWrite - bytesReadFromInput)];
bytesReadFromInput += bytesRead; bytesReadFromInput += bytesRead;
if (!bytesRead) if (!bytesRead)
usleep(500); {
if (refillNode)
[self setEndOfStream:YES];
else
usleep(500);
}
} }
bytesReadFromInput += amountToSkip; bytesReadFromInput += amountToSkip;
@ -612,7 +630,7 @@ tryagain:
inpOffset = 0; inpOffset = 0;
} }
if (floatOffset == floatSize) if (inpOffset != inpSize && floatOffset == floatSize)
{ {
struct resampler_data src_data; struct resampler_data src_data;
@ -875,6 +893,7 @@ static float db_to_scale(float db)
- (void)setOutputFormat:(AudioStreamBasicDescription)format - (void)setOutputFormat:(AudioStreamBasicDescription)format
{ {
DLog(@"SETTING OUTPUT FORMAT!"); DLog(@"SETTING OUTPUT FORMAT!");
previousOutputFormat = outputFormat;
outputFormat = format; outputFormat = format;
outputFormatChanged = YES; outputFormatChanged = YES;
} }
@ -884,13 +903,34 @@ static float db_to_scale(float db)
DLog(@"FORMAT CHANGED"); DLog(@"FORMAT CHANGED");
paused = YES; paused = YES;
[self cleanUp]; [self cleanUp];
if (outputFormatChanged) if (outputFormatChanged && ![buffer isEmpty])
{ {
[writeLock lock]; // Transfer previously buffered data, remember input format
[buffer empty]; rememberedInputFormat = format;
[writeLock unlock]; 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 - (void)setRGInfo:(NSDictionary *)rgi

View File

@ -91,7 +91,7 @@
{ {
format = *f; format = *f;
// Calculate a ratio and add to double(seconds) instead, as format may change // 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); sampleRatio = 1.0 / (format.mSampleRate * format.mBytesPerPacket);
BufferChain *bufferChain = [controller bufferChain]; BufferChain *bufferChain = [controller bufferChain];
if (bufferChain) if (bufferChain)
@ -102,8 +102,13 @@
// This clears the resampler buffer, but not the input buffer // This clears the resampler buffer, but not the input buffer
// We also have to jump the play position ahead accounting for // We also have to jump the play position ahead accounting for
// the data we are flushing // 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) if (oldSampleRatio)
amountPlayed += oldSampleRatio * [[converter buffer] bufferedLength]; amountPlayed += oldSampleRatio * [[converter buffer] bufferedLength];
#endif
[converter setOutputFormat:format]; [converter setOutputFormat:format];
[converter inputFormatDidChange:[bufferChain inputFormat]]; [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 */; }; 8389F2A4278E646E0074164C /* lpc.c in Sources */ = {isa = PBXBuildFile; fileRef = 8389F2A2278E646E0074164C /* lpc.c */; };
839366671815923C006DD712 /* CogPluginMulti.h in Headers */ = {isa = PBXBuildFile; fileRef = 839366651815923C006DD712 /* CogPluginMulti.h */; }; 839366671815923C006DD712 /* CogPluginMulti.h in Headers */ = {isa = PBXBuildFile; fileRef = 839366651815923C006DD712 /* CogPluginMulti.h */; };
839366681815923C006DD712 /* CogPluginMulti.m in Sources */ = {isa = PBXBuildFile; fileRef = 839366661815923C006DD712 /* CogPluginMulti.m */; }; 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 */; }; 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, ); }; }; 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 */; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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; }; 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>"; }; 8E8D3D2D0CBAEE6E00135C1B /* AudioContainer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AudioContainer.h; sourceTree = "<group>"; };
@ -328,6 +332,8 @@
17D21C750B8BE4BA00D1EBDE /* Chain */ = { 17D21C750B8BE4BA00D1EBDE /* Chain */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
83A44A00279119B50049B6E2 /* RefillNode.h */,
83A449FF279119B50049B6E2 /* RefillNode.m */,
17D21C760B8BE4BA00D1EBDE /* BufferChain.h */, 17D21C760B8BE4BA00D1EBDE /* BufferChain.h */,
17D21C770B8BE4BA00D1EBDE /* BufferChain.m */, 17D21C770B8BE4BA00D1EBDE /* BufferChain.m */,
8EC1225D0B993BD500C5B3AD /* ConverterNode.h */, 8EC1225D0B993BD500C5B3AD /* ConverterNode.h */,
@ -748,6 +754,7 @@
8389F292278E64590074164C /* s16_to_float.h in Headers */, 8389F292278E64590074164C /* s16_to_float.h in Headers */,
8389F279278E64590074164C /* utf.h in Headers */, 8389F279278E64590074164C /* utf.h in Headers */,
8389F288278E64590074164C /* retro_environment.h in Headers */, 8389F288278E64590074164C /* retro_environment.h in Headers */,
83A44A02279119B50049B6E2 /* RefillNode.h in Headers */,
8EC1225F0B993BD500C5B3AD /* ConverterNode.h in Headers */, 8EC1225F0B993BD500C5B3AD /* ConverterNode.h in Headers */,
8389F28C278E64590074164C /* file_stream.h in Headers */, 8389F28C278E64590074164C /* file_stream.h in Headers */,
8384912718080FF100E7332D /* Logging.h in Headers */, 8384912718080FF100E7332D /* Logging.h in Headers */,
@ -850,6 +857,7 @@
17C940240B900909008627D6 /* AudioMetadataReader.m in Sources */, 17C940240B900909008627D6 /* AudioMetadataReader.m in Sources */,
17B619310B909BC300BC003F /* AudioPropertiesReader.m in Sources */, 17B619310B909BC300BC003F /* AudioPropertiesReader.m in Sources */,
17ADB13D0B97926D00257CA2 /* AudioSource.m in Sources */, 17ADB13D0B97926D00257CA2 /* AudioSource.m in Sources */,
83A44A01279119B50049B6E2 /* RefillNode.m in Sources */,
8EC122600B993BD500C5B3AD /* ConverterNode.m in Sources */, 8EC122600B993BD500C5B3AD /* ConverterNode.m in Sources */,
8E8D3D300CBAEE6E00135C1B /* AudioContainer.m in Sources */, 8E8D3D300CBAEE6E00135C1B /* AudioContainer.m in Sources */,
B0575F300D687A4000411D77 /* Helper.m in Sources */, B0575F300D687A4000411D77 /* Helper.m in Sources */,
@ -998,9 +1006,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_NO_COMMON_BLOCKS = YES; GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = ( GCC_PREPROCESSOR_DEFINITIONS = "HAVE_CONFIG_H=1";
"HAVE_CONFIG_H=1",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES; GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNDECLARED_SELECTOR = YES; GCC_WARN_UNDECLARED_SELECTOR = YES;