Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- * audioctl.c - Command-line tool to control audio device settings (with device selection)
- * Compile with: clang -framework CoreAudio -framework AudioToolbox -o audioctl audioctl.c
- */
- #include <CoreAudio/CoreAudio.h>
- #include <AudioToolbox/AudioToolbox.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <unistd.h>
- void printUsage(const char* progName) {
- printf("Usage: %s [command] [options]\n", progName);
- printf("Commands:\n");
- printf(" list-devices List all output devices with IDs and names\n");
- printf(" get-device Get default output device ID\n");
- printf(" get-format [deviceID] Get current stream format (default device if not specified)\n");
- printf(" list-formats [deviceID] List available formats (default device if not specified)\n");
- printf(" set-format [deviceID] [rate] [bits] [channels] Set format for device\n");
- printf(" find-device [name] Find device ID by name (partial match)\n");
- }
- char* getDeviceName(AudioDeviceID deviceID) {
- UInt32 dataSize;
- AudioObjectPropertyAddress pa = {
- kAudioDevicePropertyDeviceNameCFString,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMain
- };
- CFStringRef deviceName = NULL;
- dataSize = sizeof(deviceName);
- if (AudioObjectGetPropertyData(deviceID, &pa, 0, NULL, &dataSize, &deviceName) != noErr) {
- return strdup("Unknown");
- }
- char name[256];
- if (CFStringGetCString(deviceName, name, sizeof(name), kCFStringEncodingUTF8)) {
- CFRelease(deviceName);
- return strdup(name);
- }
- CFRelease(deviceName);
- return strdup("Unknown");
- }
- void listAllOutputDevices() {
- AudioObjectPropertyAddress pa = {
- kAudioHardwarePropertyDevices,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMain
- };
- UInt32 dataSize = 0;
- if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &pa, 0, NULL, &dataSize) != noErr) {
- fprintf(stderr, "Error: Could not get device list size.\n");
- return;
- }
- UInt32 deviceCount = dataSize / sizeof(AudioDeviceID);
- AudioDeviceID* devices = (AudioDeviceID*)malloc(dataSize);
- if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa, 0, NULL, &dataSize, devices) != noErr) {
- fprintf(stderr, "Error: Could not get device list.\n");
- free(devices);
- return;
- }
- // Get default device for marking
- AudioDeviceID defaultDevice = 0;
- UInt32 defaultSize = sizeof(AudioDeviceID);
- AudioObjectPropertyAddress defaultPA = {
- kAudioHardwarePropertyDefaultOutputDevice,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMain
- };
- AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultPA, 0, NULL, &defaultSize, &defaultDevice);
- printf("=== Output Devices ===\n");
- for (UInt32 i = 0; i < deviceCount; i++) {
- // Check if device has output streams
- AudioObjectPropertyAddress streamPA = {
- kAudioDevicePropertyStreams,
- kAudioObjectPropertyScopeOutput,
- kAudioObjectPropertyElementMain
- };
- UInt32 streamSize = 0;
- if (AudioObjectGetPropertyDataSize(devices[i], &streamPA, 0, NULL, &streamSize) == noErr && streamSize > 0) {
- char* name = getDeviceName(devices[i]);
- const char* defaultMarker = (devices[i] == defaultDevice) ? " [DEFAULT]" : "";
- printf("ID: %u - %s%s\n", devices[i], name, defaultMarker);
- free(name);
- }
- }
- free(devices);
- }
- AudioDeviceID findDeviceByName(const char* searchName) {
- AudioObjectPropertyAddress pa = {
- kAudioHardwarePropertyDevices,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMain
- };
- UInt32 dataSize = 0;
- if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &pa, 0, NULL, &dataSize) != noErr) {
- return 0;
- }
- UInt32 deviceCount = dataSize / sizeof(AudioDeviceID);
- AudioDeviceID* devices = (AudioDeviceID*)malloc(dataSize);
- if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa, 0, NULL, &dataSize, devices) != noErr) {
- free(devices);
- return 0;
- }
- AudioDeviceID foundDevice = 0;
- for (UInt32 i = 0; i < deviceCount; i++) {
- // Check if device has output streams
- AudioObjectPropertyAddress streamPA = {
- kAudioDevicePropertyStreams,
- kAudioObjectPropertyScopeOutput,
- kAudioObjectPropertyElementMain
- };
- UInt32 streamSize = 0;
- if (AudioObjectGetPropertyDataSize(devices[i], &streamPA, 0, NULL, &streamSize) == noErr && streamSize > 0) {
- char* name = getDeviceName(devices[i]);
- if (strcasestr(name, searchName) != NULL) {
- printf("Found device: %s (ID: %u)\n", name, devices[i]);
- foundDevice = devices[i];
- free(name);
- break;
- }
- free(name);
- }
- }
- free(devices);
- return foundDevice;
- }
- AudioDeviceID getDefaultOutputDevice() {
- AudioDeviceID deviceID = 0;
- UInt32 dataSize = sizeof(AudioDeviceID);
- AudioObjectPropertyAddress pa = {
- kAudioHardwarePropertyDefaultOutputDevice,
- kAudioObjectPropertyScopeGlobal,
- kAudioObjectPropertyElementMain
- };
- if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa, 0, NULL, &dataSize, &deviceID) != noErr) {
- fprintf(stderr, "Error: Could not get default output device.\n");
- return 0;
- }
- return deviceID;
- }
- AudioStreamID getFirstOutputStream(AudioDeviceID deviceID) {
- AudioStreamID streams[16];
- UInt32 dataSize = sizeof(streams);
- AudioObjectPropertyAddress pa = {
- kAudioDevicePropertyStreams,
- kAudioObjectPropertyScopeOutput,
- kAudioObjectPropertyElementMain
- };
- if (AudioObjectGetPropertyData(deviceID, &pa, 0, NULL, &dataSize, streams) != noErr) {
- fprintf(stderr, "Error: Could not get streams.\n");
- return 0;
- }
- UInt32 streamCount = dataSize / sizeof(AudioStreamID);
- if (streamCount == 0) {
- fprintf(stderr, "Error: No output streams found.\n");
- return 0;
- }
- return streams[0];
- }
- void printCurrentFormat(AudioStreamID streamID) {
- AudioStreamBasicDescription format;
- UInt32 dataSize = sizeof(format);
- printf("=== Virtual Format ===\n");
- AudioObjectPropertyAddress pa = {
- kAudioStreamPropertyVirtualFormat,
- kAudioObjectPropertyScopeOutput,
- kAudioObjectPropertyElementMain
- };
- if (AudioObjectGetPropertyData(streamID, &pa, 0, NULL, &dataSize, &format) == noErr) {
- printf(" Sample Rate: %.0f Hz\n", format.mSampleRate);
- printf(" Channels: %u\n", format.mChannelsPerFrame);
- printf(" Bits per Channel: %u\n", format.mBitsPerChannel);
- printf(" Format Flags: 0x%x\n", (unsigned int)format.mFormatFlags);
- printf(" Bytes per Frame: %u\n", format.mBytesPerFrame);
- }
- printf("\n=== Physical Format ===\n");
- pa.mSelector = kAudioStreamPropertyPhysicalFormat;
- dataSize = sizeof(format);
- if (AudioObjectGetPropertyData(streamID, &pa, 0, NULL, &dataSize, &format) == noErr) {
- printf(" Sample Rate: %.0f Hz\n", format.mSampleRate);
- printf(" Channels: %u\n", format.mChannelsPerFrame);
- printf(" Bits per Channel: %u\n", format.mBitsPerChannel);
- printf(" Format Flags: 0x%x\n", (unsigned int)format.mFormatFlags);
- printf(" Bytes per Frame: %u\n", format.mBytesPerFrame);
- }
- }
- void listAvailableFormats(AudioStreamID streamID) {
- UInt32 dataSize = 0;
- printf("=== Available Physical Formats ===\n");
- AudioObjectPropertyAddress pa = {
- kAudioStreamPropertyAvailablePhysicalFormats,
- kAudioObjectPropertyScopeOutput,
- kAudioObjectPropertyElementMain
- };
- if (AudioObjectGetPropertyDataSize(streamID, &pa, 0, NULL, &dataSize) == noErr) {
- UInt32 count = dataSize / sizeof(AudioStreamRangedDescription);
- AudioStreamRangedDescription* formats = (AudioStreamRangedDescription*)malloc(dataSize);
- if (formats && AudioObjectGetPropertyData(streamID, &pa, 0, NULL, &dataSize, formats) == noErr) {
- for (UInt32 i = 0; i < count; i++) {
- AudioStreamBasicDescription f = formats[i].mFormat;
- printf(" %d: %.0f Hz, %u-bit, %u channels (flags=0x%x)\n", i,
- f.mSampleRate, f.mBitsPerChannel, f.mChannelsPerFrame,
- (unsigned int)f.mFormatFlags);
- }
- free(formats);
- }
- }
- printf("\n=== Available Virtual Formats ===\n");
- pa.mSelector = kAudioStreamPropertyAvailableVirtualFormats;
- dataSize = 0;
- if (AudioObjectGetPropertyDataSize(streamID, &pa, 0, NULL, &dataSize) == noErr) {
- UInt32 count = dataSize / sizeof(AudioStreamRangedDescription);
- AudioStreamRangedDescription* formats = (AudioStreamRangedDescription*)malloc(dataSize);
- if (formats && AudioObjectGetPropertyData(streamID, &pa, 0, NULL, &dataSize, formats) == noErr) {
- for (UInt32 i = 0; i < count; i++) {
- AudioStreamBasicDescription f = formats[i].mFormat;
- printf(" %d: %.0f Hz, %u-bit, %u channels (flags=0x%x)\n", i,
- f.mSampleRate, f.mBitsPerChannel, f.mChannelsPerFrame,
- (unsigned int)f.mFormatFlags);
- }
- free(formats);
- }
- }
- }
- int setStreamFormat(AudioStreamID streamID, Float64 rate, UInt32 bits, UInt32 channels) {
- printf("Requested format: %.0f Hz, %u-bit, %u channels\n", rate, bits, channels);
- UInt32 dataSize = 0;
- AudioObjectPropertyAddress pa = {
- kAudioStreamPropertyAvailablePhysicalFormats,
- kAudioObjectPropertyScopeOutput,
- kAudioObjectPropertyElementMain
- };
- if (AudioObjectGetPropertyDataSize(streamID, &pa, 0, NULL, &dataSize) != noErr) {
- fprintf(stderr, "Error: Could not get physical format list.\n");
- return 1;
- }
- UInt32 count = dataSize / sizeof(AudioStreamRangedDescription);
- AudioStreamRangedDescription* formats = (AudioStreamRangedDescription*)malloc(dataSize);
- if (!formats) {
- fprintf(stderr, "Error: Out of memory.\n");
- return 1;
- }
- if (AudioObjectGetPropertyData(streamID, &pa, 0, NULL, &dataSize, formats) != noErr) {
- fprintf(stderr, "Error: Could not fetch formats.\n");
- free(formats);
- return 1;
- }
- AudioStreamBasicDescription* matchedFormat = NULL;
- for (UInt32 i = 0; i < count; i++) {
- AudioStreamBasicDescription* f = &formats[i].mFormat;
- if (f->mSampleRate == rate &&
- f->mBitsPerChannel == bits &&
- f->mChannelsPerFrame == channels) {
- matchedFormat = f;
- printf("Found exact match in physical formats at index %u (flags=0x%x)\n",
- i, (unsigned int)f->mFormatFlags);
- break;
- }
- }
- if (!matchedFormat) {
- printf("No exact match in physical formats, trying virtual formats...\n");
- free(formats);
- pa.mSelector = kAudioStreamPropertyAvailableVirtualFormats;
- dataSize = 0;
- if (AudioObjectGetPropertyDataSize(streamID, &pa, 0, NULL, &dataSize) != noErr) {
- fprintf(stderr, "Error: Could not get virtual format list.\n");
- return 1;
- }
- count = dataSize / sizeof(AudioStreamRangedDescription);
- formats = (AudioStreamRangedDescription*)malloc(dataSize);
- if (!formats) {
- fprintf(stderr, "Error: Out of memory.\n");
- return 1;
- }
- if (AudioObjectGetPropertyData(streamID, &pa, 0, NULL, &dataSize, formats) != noErr) {
- fprintf(stderr, "Error: Could not fetch formats.\n");
- free(formats);
- return 1;
- }
- for (UInt32 i = 0; i < count; i++) {
- AudioStreamBasicDescription* f = &formats[i].mFormat;
- if (f->mSampleRate == rate &&
- f->mBitsPerChannel == bits &&
- f->mChannelsPerFrame == channels) {
- matchedFormat = f;
- printf("Found exact match in virtual formats at index %u (flags=0x%x)\n",
- i, (unsigned int)f->mFormatFlags);
- break;
- }
- }
- if (!matchedFormat) {
- fprintf(stderr, "\nError: No exact match found for %.0f Hz, %u-bit, %u channels\n",
- rate, bits, channels);
- fprintf(stderr, "Run 'list-formats' to see available options.\n");
- free(formats);
- return 1;
- }
- }
- pa.mSelector = kAudioStreamPropertyPhysicalFormat;
- OSStatus result = AudioObjectSetPropertyData(streamID, &pa, 0, NULL,
- sizeof(AudioStreamBasicDescription), matchedFormat);
- if (result != noErr) {
- fprintf(stderr, "Failed to set physical format (error: %d), trying virtual format...\n", result);
- pa.mSelector = kAudioStreamPropertyVirtualFormat;
- result = AudioObjectSetPropertyData(streamID, &pa, 0, NULL,
- sizeof(AudioStreamBasicDescription), matchedFormat);
- if (result != noErr) {
- fprintf(stderr, "Error: Failed to set virtual format (error: %d)\n", result);
- free(formats);
- return 1;
- }
- }
- printf("Successfully set format!\n");
- free(formats);
- usleep(100000);
- return 0;
- }
- int main(int argc, char* argv[]) {
- if (argc < 2) {
- printUsage(argv[0]);
- return 1;
- }
- if (strcmp(argv[1], "list-devices") == 0) {
- listAllOutputDevices();
- return 0;
- }
- if (strcmp(argv[1], "find-device") == 0) {
- if (argc != 3) {
- fprintf(stderr, "Usage: %s find-device [name]\n", argv[0]);
- return 1;
- }
- AudioDeviceID deviceID = findDeviceByName(argv[2]);
- if (deviceID == 0) {
- fprintf(stderr, "Device not found: %s\n", argv[2]);
- return 1;
- }
- printf("%u\n", deviceID);
- return 0;
- }
- if (strcmp(argv[1], "get-device") == 0) {
- AudioDeviceID deviceID = getDefaultOutputDevice();
- if (deviceID == 0) return 1;
- printf("%u\n", deviceID);
- return 0;
- }
- // Commands that need a device ID
- AudioDeviceID deviceID;
- int argOffset = 0;
- if (strcmp(argv[1], "get-format") == 0) {
- if (argc >= 3) {
- deviceID = atoi(argv[2]);
- argOffset = 1;
- } else {
- deviceID = getDefaultOutputDevice();
- }
- if (deviceID == 0) return 1;
- char* name = getDeviceName(deviceID);
- printf("Device: %s (ID: %u)\n\n", name, deviceID);
- free(name);
- AudioStreamID streamID = getFirstOutputStream(deviceID);
- if (streamID == 0) return 1;
- printCurrentFormat(streamID);
- } else if (strcmp(argv[1], "list-formats") == 0) {
- if (argc >= 3) {
- deviceID = atoi(argv[2]);
- argOffset = 1;
- } else {
- deviceID = getDefaultOutputDevice();
- }
- if (deviceID == 0) return 1;
- char* name = getDeviceName(deviceID);
- printf("Device: %s (ID: %u)\n\n", name, deviceID);
- free(name);
- AudioStreamID streamID = getFirstOutputStream(deviceID);
- if (streamID == 0) return 1;
- listAvailableFormats(streamID);
- } else if (strcmp(argv[1], "set-format") == 0) {
- if (argc != 6) {
- fprintf(stderr, "Usage: %s set-format [deviceID] [rate] [bits] [channels]\n", argv[0]);
- return 1;
- }
- deviceID = atoi(argv[2]);
- Float64 rate = atof(argv[3]);
- UInt32 bits = atoi(argv[4]);
- UInt32 channels = atoi(argv[5]);
- char* name = getDeviceName(deviceID);
- printf("Device: %s (ID: %u)\n", name, deviceID);
- free(name);
- AudioStreamID streamID = getFirstOutputStream(deviceID);
- if (streamID == 0) return 1;
- return setStreamFormat(streamID, rate, bits, channels);
- } else {
- fprintf(stderr, "Unknown command: %s\n", argv[1]);
- printUsage(argv[0]);
- return 1;
- }
- return 0;
- }
Advertisement
Add Comment
Please, Sign In to add comment