diff --git a/Cocoa/AudioRecordingAccessoryView.xib b/Cocoa/AudioRecordingAccessoryView.xib
new file mode 100644
index 0000000..6dda38b
--- /dev/null
+++ b/Cocoa/AudioRecordingAccessoryView.xib
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Cocoa/Document.h b/Cocoa/Document.h
index e92c180..c52a4ed 100644
--- a/Cocoa/Document.h
+++ b/Cocoa/Document.h
@@ -56,6 +56,8 @@
@property (readonly) GB_oam_info_t *oamInfo;
@property uint8_t oamCount;
@property uint8_t oamHeight;
+@property (strong) IBOutlet NSView *audioRecordingAccessoryView;
+@property (strong) IBOutlet NSPopUpButton *audioFormatButton;
+ (NSImage *) imageFromData:(NSData *)data width:(NSUInteger) width height:(NSUInteger) height scale:(double) scale;
-(uint8_t) readMemory:(uint16_t) addr;
diff --git a/Cocoa/Document.m b/Cocoa/Document.m
index c68d146..453e3c8 100644
--- a/Cocoa/Document.m
+++ b/Cocoa/Document.m
@@ -101,6 +101,9 @@ enum model {
Document *slave;
signed linkOffset;
bool linkCableBit;
+
+ NSSavePanel *_audioSavePanel;
+ bool _isRecordingAudio;
}
@property GBAudioClient *audioClient;
@@ -1134,7 +1137,7 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
return !GB_debugger_is_stopped(&gb);
}
else if ([anItem action] == @selector(reset:) && anItem.tag != MODEL_NONE) {
- [(NSMenuItem*)anItem setState:anItem.tag == current_model];
+ [(NSMenuItem *)anItem setState:anItem.tag == current_model];
}
else if ([anItem action] == @selector(interrupt:)) {
if (![[NSUserDefaults standardUserDefaults] boolForKey:@"DeveloperMode"]) {
@@ -1142,26 +1145,29 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
}
}
else if ([anItem action] == @selector(disconnectAllAccessories:)) {
- [(NSMenuItem*)anItem setState:accessory == GBAccessoryNone];
+ [(NSMenuItem *)anItem setState:accessory == GBAccessoryNone];
}
else if ([anItem action] == @selector(connectPrinter:)) {
- [(NSMenuItem*)anItem setState:accessory == GBAccessoryPrinter];
+ [(NSMenuItem *)anItem setState:accessory == GBAccessoryPrinter];
}
else if ([anItem action] == @selector(connectWorkboy:)) {
- [(NSMenuItem*)anItem setState:accessory == GBAccessoryWorkboy];
+ [(NSMenuItem *)anItem setState:accessory == GBAccessoryWorkboy];
}
else if ([anItem action] == @selector(connectLinkCable:)) {
- [(NSMenuItem*)anItem setState:[(NSMenuItem *)anItem representedObject] == master ||
+ [(NSMenuItem *)anItem setState:[(NSMenuItem *)anItem representedObject] == master ||
[(NSMenuItem *)anItem representedObject] == slave];
}
else if ([anItem action] == @selector(toggleCheats:)) {
- [(NSMenuItem*)anItem setState:GB_cheats_enabled(&gb)];
+ [(NSMenuItem *)anItem setState:GB_cheats_enabled(&gb)];
}
else if ([anItem action] == @selector(toggleDisplayBackground:)) {
- [(NSMenuItem*)anItem setState:!GB_is_background_rendering_disabled(&gb)];
+ [(NSMenuItem *)anItem setState:!GB_is_background_rendering_disabled(&gb)];
}
else if ([anItem action] == @selector(toggleDisplayObjects:)) {
- [(NSMenuItem*)anItem setState:!GB_is_object_rendering_disabled(&gb)];
+ [(NSMenuItem *)anItem setState:!GB_is_object_rendering_disabled(&gb)];
+ }
+ else if ([anItem action] == @selector(toggleAudioRecording:)) {
+ [(NSMenuItem *)anItem setTitle:_isRecordingAudio? @"Stop Audio Recording" : @"Start Audio Recording…"];
}
return [super validateUserInterfaceItem:anItem];
@@ -2388,5 +2394,70 @@ static unsigned *multiplication_table_for_frequency(unsigned frequency)
}];
}
+- (IBAction)toggleAudioRecording:(id)sender
+{
+
+ bool shouldResume = running;
+ [self stop];
+ if (_isRecordingAudio) {
+ _isRecordingAudio = false;
+ int error = GB_stop_audio_recording(&gb);
+ if (error) {
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:[NSString stringWithFormat:@"Could not finalize recording: %s", strerror(error)]];
+ [alert setAlertStyle:NSAlertStyleCritical];
+ [alert runModal];
+ }
+ else {
+ [self.osdView displayText:@"Audio recording ended"];
+ }
+ if (shouldResume) {
+ [self start];
+ }
+ return;
+ }
+ _audioSavePanel = [NSSavePanel savePanel];
+ if (!self.audioRecordingAccessoryView) {
+ [[NSBundle mainBundle] loadNibNamed:@"AudioRecordingAccessoryView" owner:self topLevelObjects:nil];
+ }
+ _audioSavePanel.accessoryView = self.audioRecordingAccessoryView;
+ [self audioFormatChanged:self.audioFormatButton];
+
+ [_audioSavePanel beginSheetModalForWindow:self.mainWindow completionHandler:^(NSInteger result) {
+ if (result == NSModalResponseOK) {
+ [_audioSavePanel orderOut:self];
+ int error = GB_start_audio_recording(&gb, _audioSavePanel.URL.fileSystemRepresentation, self.audioFormatButton.selectedTag);
+ if (error) {
+ NSAlert *alert = [[NSAlert alloc] init];
+ [alert setMessageText:[NSString stringWithFormat:@"Could not start recording: %s", strerror(error)]];
+ [alert setAlertStyle:NSAlertStyleCritical];
+ [alert runModal];
+ }
+ else {
+ [self.osdView displayText:@"Audio recording started"];
+ _isRecordingAudio = true;
+ }
+ }
+ if (shouldResume) {
+ [self start];
+ }
+ _audioSavePanel = nil;
+ }];
+}
+
+- (IBAction)audioFormatChanged:(NSPopUpButton *)sender
+{
+ switch ((GB_audio_format_t)sender.selectedTag) {
+ case GB_AUDIO_FORMAT_RAW:
+ _audioSavePanel.allowedFileTypes = @[@"raw", @"pcm"];
+ break;
+ case GB_AUDIO_FORMAT_AIFF:
+ _audioSavePanel.allowedFileTypes = @[@"aiff", @"aif", @"aifc"];
+ break;
+ case GB_AUDIO_FORMAT_WAV:
+ _audioSavePanel.allowedFileTypes = @[@"wav"];
+ break;
+ }
+}
@end
diff --git a/Cocoa/MainMenu.xib b/Cocoa/MainMenu.xib
index 3a6cff5..55e31cb 100644
--- a/Cocoa/MainMenu.xib
+++ b/Cocoa/MainMenu.xib
@@ -370,9 +370,14 @@
-