Guest User

Untitled

a guest
Dec 18th, 2025
38
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 16.48 KB | Source Code | 0 0
  1. /*
  2.  * audioctl.c - Command-line tool to control audio device settings (with device selection)
  3.  * Compile with: clang -framework CoreAudio -framework AudioToolbox -o audioctl audioctl.c
  4.  */
  5.  
  6. #include <CoreAudio/CoreAudio.h>
  7. #include <AudioToolbox/AudioToolbox.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. #include <unistd.h>
  12.  
  13. void printUsage(const char* progName) {
  14.     printf("Usage: %s [command] [options]\n", progName);
  15.     printf("Commands:\n");
  16.     printf("  list-devices           List all output devices with IDs and names\n");
  17.     printf("  get-device             Get default output device ID\n");
  18.     printf("  get-format [deviceID]  Get current stream format (default device if not specified)\n");
  19.     printf("  list-formats [deviceID] List available formats (default device if not specified)\n");
  20.     printf("  set-format [deviceID] [rate] [bits] [channels]  Set format for device\n");
  21.     printf("  find-device [name]     Find device ID by name (partial match)\n");
  22. }
  23.  
  24. char* getDeviceName(AudioDeviceID deviceID) {
  25.     UInt32 dataSize;
  26.     AudioObjectPropertyAddress pa = {
  27.         kAudioDevicePropertyDeviceNameCFString,
  28.         kAudioObjectPropertyScopeGlobal,
  29.         kAudioObjectPropertyElementMain
  30.     };
  31.  
  32.     CFStringRef deviceName = NULL;
  33.     dataSize = sizeof(deviceName);
  34.  
  35.     if (AudioObjectGetPropertyData(deviceID, &pa, 0, NULL, &dataSize, &deviceName) != noErr) {
  36.         return strdup("Unknown");
  37.     }
  38.  
  39.     char name[256];
  40.     if (CFStringGetCString(deviceName, name, sizeof(name), kCFStringEncodingUTF8)) {
  41.         CFRelease(deviceName);
  42.         return strdup(name);
  43.     }
  44.  
  45.     CFRelease(deviceName);
  46.     return strdup("Unknown");
  47. }
  48.  
  49. void listAllOutputDevices() {
  50.     AudioObjectPropertyAddress pa = {
  51.         kAudioHardwarePropertyDevices,
  52.         kAudioObjectPropertyScopeGlobal,
  53.         kAudioObjectPropertyElementMain
  54.     };
  55.  
  56.     UInt32 dataSize = 0;
  57.     if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &pa, 0, NULL, &dataSize) != noErr) {
  58.         fprintf(stderr, "Error: Could not get device list size.\n");
  59.         return;
  60.     }
  61.  
  62.     UInt32 deviceCount = dataSize / sizeof(AudioDeviceID);
  63.     AudioDeviceID* devices = (AudioDeviceID*)malloc(dataSize);
  64.  
  65.     if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa, 0, NULL, &dataSize, devices) != noErr) {
  66.         fprintf(stderr, "Error: Could not get device list.\n");
  67.         free(devices);
  68.         return;
  69.     }
  70.  
  71.     // Get default device for marking
  72.     AudioDeviceID defaultDevice = 0;
  73.     UInt32 defaultSize = sizeof(AudioDeviceID);
  74.     AudioObjectPropertyAddress defaultPA = {
  75.         kAudioHardwarePropertyDefaultOutputDevice,
  76.         kAudioObjectPropertyScopeGlobal,
  77.         kAudioObjectPropertyElementMain
  78.     };
  79.     AudioObjectGetPropertyData(kAudioObjectSystemObject, &defaultPA, 0, NULL, &defaultSize, &defaultDevice);
  80.  
  81.     printf("=== Output Devices ===\n");
  82.     for (UInt32 i = 0; i < deviceCount; i++) {
  83.         // Check if device has output streams
  84.         AudioObjectPropertyAddress streamPA = {
  85.             kAudioDevicePropertyStreams,
  86.             kAudioObjectPropertyScopeOutput,
  87.             kAudioObjectPropertyElementMain
  88.         };
  89.  
  90.         UInt32 streamSize = 0;
  91.         if (AudioObjectGetPropertyDataSize(devices[i], &streamPA, 0, NULL, &streamSize) == noErr && streamSize > 0) {
  92.             char* name = getDeviceName(devices[i]);
  93.             const char* defaultMarker = (devices[i] == defaultDevice) ? " [DEFAULT]" : "";
  94.             printf("ID: %u - %s%s\n", devices[i], name, defaultMarker);
  95.             free(name);
  96.         }
  97.     }
  98.  
  99.     free(devices);
  100. }
  101.  
  102. AudioDeviceID findDeviceByName(const char* searchName) {
  103.     AudioObjectPropertyAddress pa = {
  104.         kAudioHardwarePropertyDevices,
  105.         kAudioObjectPropertyScopeGlobal,
  106.         kAudioObjectPropertyElementMain
  107.     };
  108.  
  109.     UInt32 dataSize = 0;
  110.     if (AudioObjectGetPropertyDataSize(kAudioObjectSystemObject, &pa, 0, NULL, &dataSize) != noErr) {
  111.         return 0;
  112.     }
  113.  
  114.     UInt32 deviceCount = dataSize / sizeof(AudioDeviceID);
  115.     AudioDeviceID* devices = (AudioDeviceID*)malloc(dataSize);
  116.  
  117.     if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa, 0, NULL, &dataSize, devices) != noErr) {
  118.         free(devices);
  119.         return 0;
  120.     }
  121.  
  122.     AudioDeviceID foundDevice = 0;
  123.     for (UInt32 i = 0; i < deviceCount; i++) {
  124.         // Check if device has output streams
  125.         AudioObjectPropertyAddress streamPA = {
  126.             kAudioDevicePropertyStreams,
  127.             kAudioObjectPropertyScopeOutput,
  128.             kAudioObjectPropertyElementMain
  129.         };
  130.  
  131.         UInt32 streamSize = 0;
  132.         if (AudioObjectGetPropertyDataSize(devices[i], &streamPA, 0, NULL, &streamSize) == noErr && streamSize > 0) {
  133.             char* name = getDeviceName(devices[i]);
  134.             if (strcasestr(name, searchName) != NULL) {
  135.                 printf("Found device: %s (ID: %u)\n", name, devices[i]);
  136.                 foundDevice = devices[i];
  137.                 free(name);
  138.                 break;
  139.             }
  140.             free(name);
  141.         }
  142.     }
  143.  
  144.     free(devices);
  145.     return foundDevice;
  146. }
  147.  
  148. AudioDeviceID getDefaultOutputDevice() {
  149.     AudioDeviceID deviceID = 0;
  150.     UInt32 dataSize = sizeof(AudioDeviceID);
  151.     AudioObjectPropertyAddress pa = {
  152.         kAudioHardwarePropertyDefaultOutputDevice,
  153.         kAudioObjectPropertyScopeGlobal,
  154.         kAudioObjectPropertyElementMain
  155.     };
  156.     if (AudioObjectGetPropertyData(kAudioObjectSystemObject, &pa, 0, NULL, &dataSize, &deviceID) != noErr) {
  157.         fprintf(stderr, "Error: Could not get default output device.\n");
  158.         return 0;
  159.     }
  160.     return deviceID;
  161. }
  162.  
  163. AudioStreamID getFirstOutputStream(AudioDeviceID deviceID) {
  164.     AudioStreamID streams[16];
  165.     UInt32 dataSize = sizeof(streams);
  166.     AudioObjectPropertyAddress pa = {
  167.         kAudioDevicePropertyStreams,
  168.         kAudioObjectPropertyScopeOutput,
  169.         kAudioObjectPropertyElementMain
  170.     };
  171.  
  172.     if (AudioObjectGetPropertyData(deviceID, &pa, 0, NULL, &dataSize, streams) != noErr) {
  173.         fprintf(stderr, "Error: Could not get streams.\n");
  174.         return 0;
  175.     }
  176.     UInt32 streamCount = dataSize / sizeof(AudioStreamID);
  177.     if (streamCount == 0) {
  178.         fprintf(stderr, "Error: No output streams found.\n");
  179.         return 0;
  180.     }
  181.     return streams[0];
  182. }
  183.  
  184. void printCurrentFormat(AudioStreamID streamID) {
  185.     AudioStreamBasicDescription format;
  186.     UInt32 dataSize = sizeof(format);
  187.  
  188.     printf("=== Virtual Format ===\n");
  189.     AudioObjectPropertyAddress pa = {
  190.         kAudioStreamPropertyVirtualFormat,
  191.         kAudioObjectPropertyScopeOutput,
  192.         kAudioObjectPropertyElementMain
  193.     };
  194.     if (AudioObjectGetPropertyData(streamID, &pa, 0, NULL, &dataSize, &format) == noErr) {
  195.         printf("  Sample Rate: %.0f Hz\n", format.mSampleRate);
  196.         printf("  Channels: %u\n", format.mChannelsPerFrame);
  197.         printf("  Bits per Channel: %u\n", format.mBitsPerChannel);
  198.         printf("  Format Flags: 0x%x\n", (unsigned int)format.mFormatFlags);
  199.         printf("  Bytes per Frame: %u\n", format.mBytesPerFrame);
  200.     }
  201.  
  202.     printf("\n=== Physical Format ===\n");
  203.     pa.mSelector = kAudioStreamPropertyPhysicalFormat;
  204.     dataSize = sizeof(format);
  205.     if (AudioObjectGetPropertyData(streamID, &pa, 0, NULL, &dataSize, &format) == noErr) {
  206.         printf("  Sample Rate: %.0f Hz\n", format.mSampleRate);
  207.         printf("  Channels: %u\n", format.mChannelsPerFrame);
  208.         printf("  Bits per Channel: %u\n", format.mBitsPerChannel);
  209.         printf("  Format Flags: 0x%x\n", (unsigned int)format.mFormatFlags);
  210.         printf("  Bytes per Frame: %u\n", format.mBytesPerFrame);
  211.     }
  212. }
  213.  
  214. void listAvailableFormats(AudioStreamID streamID) {
  215.     UInt32 dataSize = 0;
  216.  
  217.     printf("=== Available Physical Formats ===\n");
  218.     AudioObjectPropertyAddress pa = {
  219.         kAudioStreamPropertyAvailablePhysicalFormats,
  220.         kAudioObjectPropertyScopeOutput,
  221.         kAudioObjectPropertyElementMain
  222.     };
  223.  
  224.     if (AudioObjectGetPropertyDataSize(streamID, &pa, 0, NULL, &dataSize) == noErr) {
  225.         UInt32 count = dataSize / sizeof(AudioStreamRangedDescription);
  226.         AudioStreamRangedDescription* formats = (AudioStreamRangedDescription*)malloc(dataSize);
  227.         if (formats && AudioObjectGetPropertyData(streamID, &pa, 0, NULL, &dataSize, formats) == noErr) {
  228.             for (UInt32 i = 0; i < count; i++) {
  229.                 AudioStreamBasicDescription f = formats[i].mFormat;
  230.                 printf("  %d: %.0f Hz, %u-bit, %u channels (flags=0x%x)\n", i,
  231.                        f.mSampleRate, f.mBitsPerChannel, f.mChannelsPerFrame,
  232.                        (unsigned int)f.mFormatFlags);
  233.             }
  234.             free(formats);
  235.         }
  236.     }
  237.  
  238.     printf("\n=== Available Virtual Formats ===\n");
  239.     pa.mSelector = kAudioStreamPropertyAvailableVirtualFormats;
  240.     dataSize = 0;
  241.  
  242.     if (AudioObjectGetPropertyDataSize(streamID, &pa, 0, NULL, &dataSize) == noErr) {
  243.         UInt32 count = dataSize / sizeof(AudioStreamRangedDescription);
  244.         AudioStreamRangedDescription* formats = (AudioStreamRangedDescription*)malloc(dataSize);
  245.         if (formats && AudioObjectGetPropertyData(streamID, &pa, 0, NULL, &dataSize, formats) == noErr) {
  246.             for (UInt32 i = 0; i < count; i++) {
  247.                 AudioStreamBasicDescription f = formats[i].mFormat;
  248.                 printf("  %d: %.0f Hz, %u-bit, %u channels (flags=0x%x)\n", i,
  249.                        f.mSampleRate, f.mBitsPerChannel, f.mChannelsPerFrame,
  250.                        (unsigned int)f.mFormatFlags);
  251.             }
  252.             free(formats);
  253.         }
  254.     }
  255. }
  256.  
  257. int setStreamFormat(AudioStreamID streamID, Float64 rate, UInt32 bits, UInt32 channels) {
  258.     printf("Requested format: %.0f Hz, %u-bit, %u channels\n", rate, bits, channels);
  259.  
  260.     UInt32 dataSize = 0;
  261.     AudioObjectPropertyAddress pa = {
  262.         kAudioStreamPropertyAvailablePhysicalFormats,
  263.         kAudioObjectPropertyScopeOutput,
  264.         kAudioObjectPropertyElementMain
  265.     };
  266.  
  267.     if (AudioObjectGetPropertyDataSize(streamID, &pa, 0, NULL, &dataSize) != noErr) {
  268.         fprintf(stderr, "Error: Could not get physical format list.\n");
  269.         return 1;
  270.     }
  271.  
  272.     UInt32 count = dataSize / sizeof(AudioStreamRangedDescription);
  273.     AudioStreamRangedDescription* formats = (AudioStreamRangedDescription*)malloc(dataSize);
  274.     if (!formats) {
  275.         fprintf(stderr, "Error: Out of memory.\n");
  276.         return 1;
  277.     }
  278.  
  279.     if (AudioObjectGetPropertyData(streamID, &pa, 0, NULL, &dataSize, formats) != noErr) {
  280.         fprintf(stderr, "Error: Could not fetch formats.\n");
  281.         free(formats);
  282.         return 1;
  283.     }
  284.  
  285.     AudioStreamBasicDescription* matchedFormat = NULL;
  286.     for (UInt32 i = 0; i < count; i++) {
  287.         AudioStreamBasicDescription* f = &formats[i].mFormat;
  288.         if (f->mSampleRate == rate &&
  289.             f->mBitsPerChannel == bits &&
  290.             f->mChannelsPerFrame == channels) {
  291.             matchedFormat = f;
  292.             printf("Found exact match in physical formats at index %u (flags=0x%x)\n",
  293.                    i, (unsigned int)f->mFormatFlags);
  294.             break;
  295.         }
  296.     }
  297.  
  298.     if (!matchedFormat) {
  299.         printf("No exact match in physical formats, trying virtual formats...\n");
  300.         free(formats);
  301.  
  302.         pa.mSelector = kAudioStreamPropertyAvailableVirtualFormats;
  303.         dataSize = 0;
  304.  
  305.         if (AudioObjectGetPropertyDataSize(streamID, &pa, 0, NULL, &dataSize) != noErr) {
  306.             fprintf(stderr, "Error: Could not get virtual format list.\n");
  307.             return 1;
  308.         }
  309.  
  310.         count = dataSize / sizeof(AudioStreamRangedDescription);
  311.         formats = (AudioStreamRangedDescription*)malloc(dataSize);
  312.         if (!formats) {
  313.             fprintf(stderr, "Error: Out of memory.\n");
  314.             return 1;
  315.         }
  316.  
  317.         if (AudioObjectGetPropertyData(streamID, &pa, 0, NULL, &dataSize, formats) != noErr) {
  318.             fprintf(stderr, "Error: Could not fetch formats.\n");
  319.             free(formats);
  320.             return 1;
  321.         }
  322.  
  323.         for (UInt32 i = 0; i < count; i++) {
  324.             AudioStreamBasicDescription* f = &formats[i].mFormat;
  325.             if (f->mSampleRate == rate &&
  326.                 f->mBitsPerChannel == bits &&
  327.                 f->mChannelsPerFrame == channels) {
  328.                 matchedFormat = f;
  329.                 printf("Found exact match in virtual formats at index %u (flags=0x%x)\n",
  330.                        i, (unsigned int)f->mFormatFlags);
  331.                 break;
  332.             }
  333.         }
  334.  
  335.         if (!matchedFormat) {
  336.             fprintf(stderr, "\nError: No exact match found for %.0f Hz, %u-bit, %u channels\n",
  337.                     rate, bits, channels);
  338.             fprintf(stderr, "Run 'list-formats' to see available options.\n");
  339.             free(formats);
  340.             return 1;
  341.         }
  342.     }
  343.  
  344.     pa.mSelector = kAudioStreamPropertyPhysicalFormat;
  345.     OSStatus result = AudioObjectSetPropertyData(streamID, &pa, 0, NULL,
  346.                                                   sizeof(AudioStreamBasicDescription), matchedFormat);
  347.  
  348.     if (result != noErr) {
  349.         fprintf(stderr, "Failed to set physical format (error: %d), trying virtual format...\n", result);
  350.  
  351.         pa.mSelector = kAudioStreamPropertyVirtualFormat;
  352.         result = AudioObjectSetPropertyData(streamID, &pa, 0, NULL,
  353.                                            sizeof(AudioStreamBasicDescription), matchedFormat);
  354.  
  355.         if (result != noErr) {
  356.             fprintf(stderr, "Error: Failed to set virtual format (error: %d)\n", result);
  357.             free(formats);
  358.             return 1;
  359.         }
  360.     }
  361.  
  362.     printf("Successfully set format!\n");
  363.     free(formats);
  364.     usleep(100000);
  365.     return 0;
  366. }
  367.  
  368. int main(int argc, char* argv[]) {
  369.     if (argc < 2) {
  370.         printUsage(argv[0]);
  371.         return 1;
  372.     }
  373.  
  374.     if (strcmp(argv[1], "list-devices") == 0) {
  375.         listAllOutputDevices();
  376.         return 0;
  377.     }
  378.  
  379.     if (strcmp(argv[1], "find-device") == 0) {
  380.         if (argc != 3) {
  381.             fprintf(stderr, "Usage: %s find-device [name]\n", argv[0]);
  382.             return 1;
  383.         }
  384.         AudioDeviceID deviceID = findDeviceByName(argv[2]);
  385.         if (deviceID == 0) {
  386.             fprintf(stderr, "Device not found: %s\n", argv[2]);
  387.             return 1;
  388.         }
  389.         printf("%u\n", deviceID);
  390.         return 0;
  391.     }
  392.  
  393.     if (strcmp(argv[1], "get-device") == 0) {
  394.         AudioDeviceID deviceID = getDefaultOutputDevice();
  395.         if (deviceID == 0) return 1;
  396.         printf("%u\n", deviceID);
  397.         return 0;
  398.     }
  399.  
  400.     // Commands that need a device ID
  401.     AudioDeviceID deviceID;
  402.     int argOffset = 0;
  403.  
  404.     if (strcmp(argv[1], "get-format") == 0) {
  405.         if (argc >= 3) {
  406.             deviceID = atoi(argv[2]);
  407.             argOffset = 1;
  408.         } else {
  409.             deviceID = getDefaultOutputDevice();
  410.         }
  411.         if (deviceID == 0) return 1;
  412.  
  413.         char* name = getDeviceName(deviceID);
  414.         printf("Device: %s (ID: %u)\n\n", name, deviceID);
  415.         free(name);
  416.  
  417.         AudioStreamID streamID = getFirstOutputStream(deviceID);
  418.         if (streamID == 0) return 1;
  419.         printCurrentFormat(streamID);
  420.  
  421.     } else if (strcmp(argv[1], "list-formats") == 0) {
  422.         if (argc >= 3) {
  423.             deviceID = atoi(argv[2]);
  424.             argOffset = 1;
  425.         } else {
  426.             deviceID = getDefaultOutputDevice();
  427.         }
  428.         if (deviceID == 0) return 1;
  429.  
  430.         char* name = getDeviceName(deviceID);
  431.         printf("Device: %s (ID: %u)\n\n", name, deviceID);
  432.         free(name);
  433.  
  434.         AudioStreamID streamID = getFirstOutputStream(deviceID);
  435.         if (streamID == 0) return 1;
  436.         listAvailableFormats(streamID);
  437.  
  438.     } else if (strcmp(argv[1], "set-format") == 0) {
  439.         if (argc != 6) {
  440.             fprintf(stderr, "Usage: %s set-format [deviceID] [rate] [bits] [channels]\n", argv[0]);
  441.             return 1;
  442.         }
  443.         deviceID = atoi(argv[2]);
  444.         Float64 rate = atof(argv[3]);
  445.         UInt32 bits = atoi(argv[4]);
  446.         UInt32 channels = atoi(argv[5]);
  447.  
  448.         char* name = getDeviceName(deviceID);
  449.         printf("Device: %s (ID: %u)\n", name, deviceID);
  450.         free(name);
  451.  
  452.         AudioStreamID streamID = getFirstOutputStream(deviceID);
  453.         if (streamID == 0) return 1;
  454.         return setStreamFormat(streamID, rate, bits, channels);
  455.  
  456.     } else {
  457.         fprintf(stderr, "Unknown command: %s\n", argv[1]);
  458.         printUsage(argv[0]);
  459.         return 1;
  460.     }
  461.  
  462.     return 0;
  463. }
  464.  
Tags: mac CLI audio
Advertisement
Add Comment
Please, Sign In to add comment