How to read the Master File Table (MFT) using C? [closed] - c

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I want to read the MFT of my local system using C.
I search for solution all over the internet but nothing found.
I hope someone has a tutorial for me or a good explanation with example of code about how to do that.
Thanks in advance.

first of all we need open volume handle we FILE_READ_DATA.
then we need query NTFS_VOLUME_DATA_BUFFER with FSCTL_GET_NTFS_VOLUME_DATA control code
from here we got size of single MFT record - BytesPerFileRecordSegment, total size of MFT - MftValidDataLength. so maximum record count is (MftValidDataLength.QuadPart / BytesPerFileRecordSegment)
correct way (synchronized with ntfs) will be read single record via FSCTL_GET_NTFS_FILE_RECORD.
if want read multiple records at once - of course possible read direct from volume. we have the start LCN for MFT - MftStartLcn. but MFT can have several not continuos fragments. so we need use FSCTL_GET_RETRIEVAL_POINTERS if want got all fragments locations. for convert LCN to volume offset need multiple it to BytesPerCluster
demo code:
void ReadMft(PCWSTR szVolume)
{
HANDLE hVolume = CreateFileW(szVolume, FILE_READ_DATA, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_OPEN_FOR_BACKUP_INTENT, 0);
if (hVolume != INVALID_HANDLE_VALUE)
{
NTFS_VOLUME_DATA_BUFFER nvdb;
OVERLAPPED ov = {};
if (DeviceIoControl(hVolume, FSCTL_GET_NTFS_VOLUME_DATA, 0, 0, &nvdb, sizeof(nvdb), 0, &ov))
{
NTFS_FILE_RECORD_INPUT_BUFFER nfrib;
nfrib.FileReferenceNumber.QuadPart = nvdb.MftValidDataLength.QuadPart / nvdb.BytesPerFileRecordSegment - 1;
ULONG cb = __builtin_offsetof(NTFS_FILE_RECORD_OUTPUT_BUFFER, FileRecordBuffer[nvdb.BytesPerFileRecordSegment]);
PNTFS_FILE_RECORD_OUTPUT_BUFFER pnfrob = (PNTFS_FILE_RECORD_OUTPUT_BUFFER)alloca(cb);
do
{
if (!DeviceIoControl(hVolume, FSCTL_GET_NTFS_FILE_RECORD,
&nfrib, sizeof(nfrib), pnfrob, cb, 0, &ov))
{
break;
}
// pnfrob->FileRecordBuffer :
// here pnfrob->FileReferenceNumber FileRecord
} while (0 <= (nfrib.FileReferenceNumber.QuadPart = pnfrob->FileReferenceNumber.QuadPart - 1));
ReadMft2(szVolume, hVolume, &nvdb);
}
CloseHandle(hVolume);
}
}
void ReadMft2(PCWSTR szVolume, HANDLE hVolume, PNTFS_VOLUME_DATA_BUFFER nvdb)
{
static PCWSTR MFT = L"\\$MFT";
static STARTING_VCN_INPUT_BUFFER vcn {};
static volatile UCHAR guz;
PVOID stack = alloca(guz);
union {
PVOID buf;
PWSTR lpFileName;
PRETRIEVAL_POINTERS_BUFFER rpb;
};
buf = alloca(wcslen(szVolume) * sizeof(WCHAR) + sizeof(MFT));
wcscat(wcscpy(lpFileName, szVolume), MFT);
HANDLE hFile = CreateFileW(lpFileName, 0, FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, FILE_OPEN_FOR_BACKUP_INTENT, 0);
if (hFile != INVALID_HANDLE_VALUE)
{
OVERLAPPED ov{};
ULONG cb = RtlPointerToOffset(buf, stack), rcb, ExtentCount = 2;
do
{
rcb = __builtin_offsetof(RETRIEVAL_POINTERS_BUFFER, Extents[ExtentCount]);
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (DeviceIoControl(hFile, FSCTL_GET_RETRIEVAL_POINTERS, &vcn, sizeof(vcn), buf, cb, 0, &ov))
{
if (rpb->Extents->Lcn.QuadPart != nvdb->MftStartLcn.QuadPart)
{
__debugbreak();
}
if (ExtentCount = rpb->ExtentCount)
{
auto Extents = rpb->Extents;
ULONG BytesPerCluster = nvdb->BytesPerCluster;
ULONG BytesPerFileRecordSegment = nvdb->BytesPerFileRecordSegment;
LONGLONG StartingVcn = rpb->StartingVcn.QuadPart, NextVcn, len;
PVOID FileRecordBuffer = alloca(BytesPerFileRecordSegment);
do
{
NextVcn = Extents->NextVcn.QuadPart;
len = NextVcn - StartingVcn, StartingVcn = NextVcn;
DbgPrint("%I64x %I64x\n", Extents->Lcn.QuadPart, len);
if (Extents->Lcn.QuadPart != -1)
{
Extents->Lcn.QuadPart *= BytesPerCluster;
ov.Offset = Extents->Lcn.LowPart;
ov.OffsetHigh = Extents->Lcn.HighPart;
// read 1 record
ReadFile(hVolume, FileRecordBuffer, BytesPerFileRecordSegment, 0, &ov);
}
} while (Extents++, --ExtentCount);
}
break;
}
ExtentCount <<= 1;
} while (GetLastError() == ERROR_MORE_DATA);
CloseHandle(hFile);
}
}

The cluster number of the start of the MFT is at offset 0x30 of the NTFS boot sector as described here.
GetDiskFreeSpace will return you the cluster size, and to open a volume for direct access you can do (for example) CreateFile ("\\.\C:", ...) as described here under 'Physical Disks and Volumes'.

Related

WINAPI determine if specific SCSI device is present

I'm currently trying to detect if a machine has a specific NVME SSD installed. On my machine, the file name is: SCSI#Disk&Ven_NVMe&Prod_XPG_SPECTRIX_S40#5&1363da6c&0&000000#{53f56307-b6bf-11d0-94f2-00a0c91efb8b}
What would be a reliable way to detect this universally on any machine? I don't know if any of these fields are unique to each machine, and I'm not very familiar with Windows' API.
when disk type hardware is detected, kernel create device for it (really 2 device usually PDO and FDO attached to it) and register well known interface GUID_DEVINTERFACE_DISK (by call IoRegisterDeviceInterface ). as result Symbolic Link will be created under \GLOBAL?? directory. we can got all registered disk interfaces via CM_Get_Device_Interface_ListW.
SCSI#Disk&Ven_NVMe&Prod_XPG_SPECTRIX_S40# obvious unique and will be the same anywhere for this device.
so code can be next
CONFIGRET Is_XPG_SPECTRIX_S40_Present(PBOOL pb)
{
CONFIGRET cr;
ULONG cb = 0, rcb;
union {
PVOID buf;
PZZWSTR Buffer;
};
static volatile UCHAR guz = 0;
PVOID stack = alloca(guz);
do
{
cr = CM_Get_Device_Interface_List_SizeW(&rcb, const_cast<GUID*>(&GUID_DEVINTERFACE_DISK), 0, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
if (cr != CR_SUCCESS)
{
break;
}
if (cb < (rcb *= sizeof(WCHAR)))
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);// /RTCs must be off
}
cr = CM_Get_Device_Interface_ListW(const_cast<GUID*>(&GUID_DEVINTERFACE_DISK),
0, Buffer, cb, CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
} while (cr == CR_BUFFER_SMALL);
if (cr == CR_SUCCESS)
{
while (*Buffer)
{
if (wcsstr(Buffer, L"\\SCSI#Disk&Ven_NVMe&Prod_XPG_SPECTRIX_S40#"))
{
*pb = TRUE;
break;
}
Buffer += wcslen(Buffer) + 1;
}
}
return cr;
}

How to read columns names in table in ESE using C language and esent.lib?

I need a code example. I'd like to see how we can enumerate columns names in table. (It's essential for me to use esent.dll/esent.lib and C language)
I tried to use attached code (found a guide but it doesn't work as I expect).
JET_COLUMNLIST column_info;
JET_RETRIEVECOLUMN j_rc[4];
err = JetGetTableColumnInfo(sessionID, curr_table.tableID, NULL, &column_info, sizeof(JET_COLUMNLIST), JET_ColInfoList);
j_rc[0].columnid = column_info.columnidcolumnname;
j_rc[0].cbData = sizeof(char)*JET_cbNameMost;
j_rc[0].itagSequence = 1;
j_rc[0].grbit = 0;
char buf[JET_cbNameMost] = { 0 };
j_rc[0].pvData = buf;
printf("\nRetrieving columns information:\n");
printf("Row\tId\tType\tName:\n");
unsigned long columns_qnt = 0;
for (err = JetMove(sessionID, curr_table.tableID, JET_MoveFirst, 0);
JET_errSuccess == err;
err = JetMove(sessionID, curr_table.tableID, JET_MoveNext, 0))
{
err = JetRetrieveColumns(sessionID, curr_table.tableID, j_rc, 4);
columns_qnt++;
printf("%u\t%s\n", columns_qnt, buf);
memset(buf, 0, JET_cbNameMost);
}
Please show an example. If you know good guides for ESE C programming or just some resources with describing of how it works, please share it with me. (Despite I googled a lot, don't be shy to share obvious for you resourses)
Inside table "MSysObjects" (which exists in every ESE database as service table) are 2 interisting for us columns: "Type" and "Name".
JetOpenTable(sessionID, dbid, "MSysObjects", NULL, NULL, JET_bitTableSequential, &tableID);
JET_COLUMNBASE j_cb_name, j_cb_type, j_cb_coltype;
JetGetColumnInfo(sessionID, dbid, "MSysObjects", "Name", &j_cb_name, sizeof(JET_COLUMNBASE), JET_ColInfoBase);
JetGetColumnInfo(sessionID, dbid, "MSysObjects", "Type", &j_cb_type, sizeof(JET_COLUMNBASE), JET_ColInfoBase);
JET_RETRIEVECOLUMN j_rc[2];
Here we fill structure JET_RETRIEVECOLUMN to get this 2 columns by JetRetrieveColumns
j_rc[0].columnid = j_cb_name.columnid;
j_rc[0].cbData = 1024;
j_rc[0].itagSequence = 1;
j_rc[0].grbit = NULL;
char buf[1024] = { 0 };
j_rc[0].pvData = buf;
j_rc[1].columnid = j_cb_type.columnid;
j_rc[1].cbData = sizeof(unsigned short);
j_rc[1].itagSequence = 1;
j_rc[1].grbit = NULL;
unsigned short type;
j_rc[1].pvData = &type;
for (err = JetMove(sessionID, root_tableID, JET_MoveFirst, 0);
JET_errSuccess == err;
err = JetMove(sessionID, root_tableID, JET_MoveNext, 0))
{
JetRetrieveColumns(sessionID, root_tableID, j_rc, 2);
We got them here. If type == 1 it means, that record we got is describing a table and if type == 2, then it's describing a column . (There are also other types) There is strict order, first you will get record with type 1 (table) then you will get records with type 2 that describes columns of that table (in that moment buf keeps column name), then you can get records with other types (except type == 1) that refers to that table. And finally you will get record with type 1, that means that next information we get is about another table.
}
Feel free to say that my english is awful and I wrote some junk, I'll try to explain in other way then:)
If you just want a list of column names for a particular table without using MSysObjects, here's my approach. The temporary table created by "JetGetTableColumnInfo" contains only the column ID and column Name, so it's pretty fast:
JET_ERR GetEseTableColumnNames(JET_SESID hEseSession, JET_TABLEID hEseTable)
{ JET_ERR rc;
JET_COLUMNLIST cl { };
/* Sort order for the temporary table is column name order */
rc = ::JetGetTableColumnInfo(hEseSession, hEseTable, nullptr, &cl, sizeof(cl), JET_ColInfoList | JET_ColInfoGrbitMinimalInfo);
/* Temporary table ("cl.tableid") is opened and positioned on first record */
if (rc == JET_errSuccess && cl.cRecord > 0)
{ wchar_t wszColumnName[MAX_ESE_OBJECT_NAME + 1]; // ESE doesn't play well with std::strings
unsigned long cbActual;
for (uint32_t i = 0; i < cl.cRecord; ++i)
{
rc = ::JetRetrieveColumn(hEseSession, cl.tableid, cl.columnidcolumnname, wszColumnName, sizeof(wszColumnName), &cbActual, 0, nullptr);
if (rc == JET_errSuccess)
{
/* ESE does not null terminate strings */
wszColumnName[cbActual / sizeof(wchar_t)] = L'\0';
//********
// Okay, so do something with the column name here
//********
/* Next record in temporary table */
if (i < cl.cRecord - 1)
::JetMove(hEseSession, cl.tableid, JET_MoveNext, 0);
}
else
break;
}
}
/* Close the temporary table */
::JetCloseTable(hEseSession, cl.tableid);
return rc;
}
I know other folks use MSysObjects to short-cut the process, but this works fine for me. And yes, my code looks old fashioned - I'm stuck in Hungarian!

How to get module image name from an arbitrary address in Windows kernel space?

I'm trying to see how I can get a loaded module image name from an arbitrary address from the kernel code.
In user mode I would do this:
void* pAddr;
VOID* pBase;
WCHAR buff[MAX_PATH] = {0};
//Get address of some function in some module (just to test it)
pAddr = GetProcAddress(GetModuleHandle(L"kernel32.dll"), "GetCurrentProcess");
//Get module base address
RtlPcToFileHeader(pAddr, &pBase);
//Get module image file name
GetModuleFileNameEx(GetCurrentProcess(), (HMODULE)pBase, buff, SIZEOF(buff));
Is there a way to do the same in kernel mode if I have pAddr that can point to some address in kernel or user space?
EDIT: While waiting for the answer I came up with my own code (using undocumented way of traversing PEB):
#ifdef CALLING_FROM_KERNEL_MODE
//Kernel mode
TEB* pTEB = (TEB*)PsGetCurrentThreadTeb();
#else
//User mode
#if defined(_M_X64)
//64-bit
TEB* pTEB = reinterpret_cast<TEB*>(__readgsqword(reinterpret_cast<DWORD_PTR>(&static_cast<NT_TIB*>(nullptr)->Self)));
#else
//32-bit
TEB* pTEB = reinterpret_cast<TEB*>(__readfsdword(reinterpret_cast<DWORD_PTR>(&static_cast<NT_TIB*>(nullptr)->Self)));
#endif
#endif
PEB* p_PEB = pTEB->ProcessEnvironmentBlock;
PEB_LDR_DATA* pPLD = p_PEB->Ldr;
const WCHAR* pModName = NULL;
LIST_ENTRY* pLE = &pPLD->InMemoryOrderModuleList;
LIST_ENTRY* pLE_Head = pLE;
while(pLE_Head != pLE->Flink)
{
PLDR_DATA_TABLE_ENTRY pLDTE = CONTAINING_RECORD(pLE, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
size_t szcbSizeOfImg = (size_t)pLDTE->Reserved3[1];
if((size_t)pAddr - (size_t)pLDTE->DllBase < szcbSizeOfImg)
{
pModName = pLDTE->FullDllName.Buffer;
break;
}
pLE = pLE->Flink;
}
The problem is that although it works from a user-mode, from a kernel mode PsGetCurrentThreadTeb() seems to return NULL. Does this mean kernel threads do not have a TEB?
this can be done by creating list of all loaded modules via ZwQuerySystemInformation with SystemModuleInformation
void fgt(PVOID *Callers, ULONG Count)
{
NTSTATUS status;
ULONG cb = 0x10000;
do
{
status = STATUS_INSUFFICIENT_RESOURCES;
if (PRTL_PROCESS_MODULES prpm = (PRTL_PROCESS_MODULES)ExAllocatePool(PagedPool, cb))
{
if (0 <= (status = NtQuerySystemInformation(SystemModuleInformation, prpm, cb, &cb)))
{
do
{
PVOID Caller = *Callers++;
if (ULONG NumberOfModules = prpm->NumberOfModules)
{
PRTL_PROCESS_MODULE_INFORMATION Modules = prpm->Modules;
do
{
if ((SIZE_T)Caller - (SIZE_T)Modules->ImageBase < Modules->ImageSize)
{
DbgPrint("%p> %s\n", Caller, Modules->FullPathName);
break;
}
} while (Modules++, --NumberOfModules);
}
} while (--Count);
}
ExFreePool(prpm);
}
} while (status == STATUS_INFO_LENGTH_MISMATCH);
}

How to disable WOW64 file system redirection for GetModuleFileNameEx?

I'm running the following from a 32-bit process on a 64-bit Windows 10:
#ifndef _DEBUG
WCHAR buffPath[MAX_PATH] = {0};
FARPROC pfn = (FARPROC)::GetModuleHandleEx;
HMODULE hMod = NULL;
::GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
(LPCTSTR)pfn, &hMod);
PVOID pOldVal = NULL;
if(::Wow64DisableWow64FsRedirection(&pOldVal))
{
::GetModuleFileNameEx(::GetCurrentProcess(), hMod, buffPath, _countof(buffPath));
::Wow64RevertWow64FsRedirection(pOldVal);
wprintf(L"Path=%s\n", buffPath);
}
#else
#error run_in_release_mode
#endif
and I'm expecting to receive the path as c:\windows\syswow64\KERNEL32.DLL, but it gives me:
Path=C:\Windows\System32\KERNEL32.DLL
Any idea why?
when we load dll via LoadLibrary[Ex] or LdrLoadDll - first some pre-process transmitted dll name (say convert api-* to actual dll name or redirect dll name based on manifest - well known example comctl32.dll) and then use this (possible modified) dll name to load file as dll. but wow fs redirection - not preprocessed at this stage. if dll was successfully loaded - system allocate LDR_DATA_TABLE_ENTRY structure and save transmitted (after pre-process) dll name here as is.
the GetModuleFileNameEx simply walk throughout LDR_DATA_TABLE_ENTRY double linked list and search entry where DllBase == hModule - if found - copy FullDllName to lpFilename (if buffer big enough). so it simply return dll path used during load dll. the Wow64DisableWow64FsRedirection have no any effect to this call.
if we want get real (canonical) full path to dll - need use GetMappedFileNameW or ZwQueryVirtualMemory with MemoryMappedFilenameInformation
so code can be (if we hope that MAX_PATH is enough)
WCHAR path[MAX_PATH];
GetMappedFileNameW(NtCurrentProcess(), hmod, path, RTL_NUMBER_OF(path));
or if use ntapi and correct handle any path length:
NTSTATUS GetDllName(PVOID AddressInDll, PUNICODE_STRING NtImageName)
{
NTSTATUS status;
union {
PVOID buf;
PUNICODE_STRING ImageName;
};
static volatile UCHAR guz;
PVOID stack = alloca(guz);
SIZE_T cb = 0, rcb = 0x200;
do
{
if (cb < rcb)
{
cb = RtlPointerToOffset(buf = alloca(rcb - cb), stack);
}
if (0 <= (status = ZwQueryVirtualMemory(NtCurrentProcess(), AddressInDll,
MemoryMappedFilenameInformation, buf, cb, &rcb)))
{
return RtlDuplicateUnicodeString(
RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
ImageName, NtImageName);
}
} while (status == STATUS_BUFFER_OVERFLOW);
return status;
}
UNICODE_STRING NtImageName;
if (0 <= GetDllName(hmod, &NtImageName))
{
RtlFreeUnicodeString(&NtImageName);
}
about question "way to convert it to the win32 form" - there is a counter question - for what ?
at first we can use it as is with NtOpenFile (well documented api), at second - the simplest way convert to win32 form, accepted by CreateFileW - add \\?\globalroot prefix to nt path. but not all win32 api (primary shell api) accept this form. if we want exactly dos-device form path (aka X:) need use IOCTL_MOUNTMGR_QUERY_POINTS - got the array of MOUNTMGR_MOUNT_POINT inside MOUNTMGR_MOUNT_POINTS structure and search for DeviceName which is prefix for our nt path and SymbolicLinkName have driver letter form. code can be ~
#include <mountmgr.h>
ULONG NtToDosPath(HANDLE hMM, PUNICODE_STRING ImageName, PWSTR* ppsz)
{
static MOUNTMGR_MOUNT_POINT MountPoint;
static volatile UCHAR guz;
PVOID stack = alloca(guz);
PMOUNTMGR_MOUNT_POINTS pmmp = 0;
DWORD cb = 0, rcb = 0x200, BytesReturned;
ULONG err = NOERROR;
do
{
if (cb < rcb) cb = RtlPointerToOffset(pmmp = (PMOUNTMGR_MOUNT_POINTS)alloca(rcb - cb), stack);
if (DeviceIoControl(hMM, IOCTL_MOUNTMGR_QUERY_POINTS,
&MountPoint, sizeof(MOUNTMGR_MOUNT_POINT),
pmmp, cb, &BytesReturned, 0))
{
if (ULONG NumberOfMountPoints = pmmp->NumberOfMountPoints)
{
PMOUNTMGR_MOUNT_POINT MountPoints = pmmp->MountPoints;
do
{
UNICODE_STRING SymbolicLinkName = {
MountPoints->SymbolicLinkNameLength,
SymbolicLinkName.Length,
(PWSTR)RtlOffsetToPointer(pmmp, MountPoints->SymbolicLinkNameOffset)
};
UNICODE_STRING DeviceName = {
MountPoints->DeviceNameLength,
DeviceName.Length,
(PWSTR)RtlOffsetToPointer(pmmp, MountPoints->DeviceNameOffset)
};
PWSTR FsPath;
if (RtlPrefixUnicodeString(&DeviceName, ImageName, TRUE) &&
DeviceName.Length < ImageName->Length &&
*(FsPath = (PWSTR)RtlOffsetToPointer(ImageName->Buffer, DeviceName.Length)) == '\\' &&
MOUNTMGR_IS_DRIVE_LETTER(&SymbolicLinkName))
{
cb = ImageName->Length - DeviceName.Length;
if (PWSTR psz = new WCHAR[3 + cb/sizeof(WCHAR)])
{
*ppsz = psz;
psz[0] = SymbolicLinkName.Buffer[12];
psz[1] = ':';
memcpy(psz + 2, FsPath, cb + sizeof(WCHAR));
return NOERROR;
}
return ERROR_NO_SYSTEM_RESOURCES;
}
} while (MountPoints++, --NumberOfMountPoints);
}
return ERROR_NOT_FOUND;
}
rcb = pmmp->Size;
} while ((err = GetLastError()) == ERROR_MORE_DATA);
return err;
}
ULONG NtToDosPath(PWSTR lpFilename, PWSTR* ppsz)
{
HANDLE hMM = CreateFile(MOUNTMGR_DOS_DEVICE_NAME, FILE_GENERIC_READ,
FILE_SHARE_VALID_FLAGS, 0, OPEN_EXISTING, 0, 0);
if (hMM == INVALID_HANDLE_VALUE)
{
return GetLastError();
}
UNICODE_STRING us;
RtlInitUnicodeString(&us, lpFilename);
ULONG err = NtToDosPath(hMM, &us, ppsz);
CloseHandle(hMM);
return err;
}
PWSTR psz;
if (NtToDosPath(path, &psz) == NOERROR)
{
DbgPrint("%S\n", psz);
delete [] psz;
}
also we can change in code to MOUNTMGR_IS_VOLUME_NAME(&SymbolicLinkName) for get volume (persistent) name form instead driver letter form

How to access individual channels with core audio from an external audio interface

I am learning CoreAudio and I am just going through some of the examples on Apple's documentation and figuring out how to set things up and what not. So far I am able to connect to the default connected audio input device and output it to the default output device. I connected a 2 channel interface and was able to output the input from it and output it as well.
However I was searching through their API references and examples, but could not find any thing substantial to access the individual input channels from my interface.
I was able to hack away and extract the samples from the AudioBufferList in my Render Callback function and manipulate it that way but I am wondering if there is a correct way or a more official way of accessing the data from each individual input channels.
EDIT:
This is the user data that I found from an example that I am using:
typedef struct MyAUGraphPlayer
{
AudioStreamBasicDescription streamFormat;
AUGraph graph;
AudioUnit inputUnit;
AudioUnit outputUnit;
AudioBufferList * inputBuffer;
CARingBuffer * ringBuffer;
Float64 firstInputSampleTime;
Float64 firstOutputSampleTime;
Float64 inToOutSampleTimeOffset;
} MyAUGraphPlayer;
This is how i set up the input unit:
void CreateInputUnit(MyAUGraphPlayer * player)
{
//Generates a description that matches audio HAL
AudioComponentDescription inputcd = {0};
inputcd.componentType = kAudioUnitType_Output;
inputcd.componentSubType = kAudioUnitSubType_HALOutput;
inputcd.componentManufacturer = kAudioUnitManufacturer_Apple;
UInt32 deviceCount = AudioComponentCount ( &inputcd );
printf("Found %d devices\n", deviceCount);
AudioComponent comp = AudioComponentFindNext(NULL, &inputcd);
if(comp == NULL) {
printf("Can't get output unit\n");
exit(1);
}
OSStatus status;
status = AudioComponentInstanceNew(comp, &player->inputUnit);
assert(status == noErr);
//Explicitly enable Input and disable output
UInt32 disableFlag = 0;
UInt32 enableFlag = 1;
AudioUnitScope outputBus = 0;
AudioUnitScope inputBus = 1;
status = AudioUnitSetProperty(player->inputUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Input,
inputBus,
&enableFlag,
sizeof(enableFlag))
assert(status == noErr);
status = AudioUnitSetProperty(player->inputUnit,
kAudioOutputUnitProperty_EnableIO,
kAudioUnitScope_Output,
outputBus,
&disableFlag,
sizeof(enableFlag));
assert(status == noErr);
printf("Finished enabling input and disabling output on an inputUnit\n");
//Get the default Audio input Device
AudioDeviceID defaultDevice = kAudioObjectUnknown;
UInt32 propertySize = sizeof(defaultDevice);
AudioObjectPropertyAddress defaultDeviceProperty;
defaultDeviceProperty.mSelector = kAudioHardwarePropertyDefaultInputDevice;
defaultDeviceProperty.mScope = kAudioObjectPropertyScopeGlobal;
defaultDeviceProperty.mElement = kAudioObjectPropertyElementMaster;
status = AudioObjectGetPropertyData(kAudioObjectSystemObject,
&defaultDeviceProperty,
0,
NULL,
&propertySize,
&defaultDevice);
assert(status == noErr);
//Set the current device property of the AUHAL
status = AudioUnitSetProperty(player->inputUnit,
kAudioOutputUnitProperty_CurrentDevice,
kAudioUnitScope_Global,
outputBus,
&defaultDevice,
sizeof(defaultDevice));
assert(status == noErr);
//Get the AudioStreamBasicDescription from Input AUHAL
propertySize = sizeof(AudioStreamBasicDescription);
status = AudioUnitGetProperty(player->inputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
inputBus,
&player->streamFormat,
&propertySize);
assert(status == noErr);
//Adopt hardware input sample rate
AudioStreamBasicDescription deviceFormat;
status = AudioUnitGetProperty(player->inputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Input,
inputBus,
&deviceFormat,
&propertySize);
assert(status == noErr);
player->streamFormat.mSampleRate = deviceFormat.mSampleRate;
printf("Sample Rate %f...\n", deviceFormat.mSampleRate);
propertySize = sizeof(AudioStreamBasicDescription);
status = AudioUnitSetProperty(player->inputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output,
inputBus,
&player->streamFormat,
propertySize);
assert(status == noErr);
//Calculating Capture buffer size for an I/O unit
UInt32 bufferSizeFrames = 0;
propertySize = sizeof(UInt32);
status = AudioUnitGetProperty(player->inputUnit,
kAudioDevicePropertyBufferFrameSize,
kAudioUnitScope_Global,
0,
&bufferSizeFrames,
&propertySize);
assert(status == noErr);
UInt32 bufferSizeBytes = bufferSizeFrames * sizeof(Float32);
//Create AudioBufferList to receive capture data
UInt32 propSize = offsetof(AudioBufferList, mBuffers[0]) +
(sizeof(AudioBuffer) * player->streamFormat.mChannelsPerFrame);
//Malloc buffer lists
player->inputBuffer = (AudioBufferList *) malloc(propSize);
player->inputBuffer->mNumberBuffers = player->streamFormat.mChannelsPerFrame;
//Pre malloc buffers for AudioBufferLists
for(UInt32 i = 0; i < player->inputBuffer->mNumberBuffers; i++){
player->inputBuffer->mBuffers[i].mNumberChannels = 1;
player->inputBuffer->mBuffers[i].mDataByteSize = bufferSizeBytes;
player->inputBuffer->mBuffers[i].mData = malloc(bufferSizeBytes);
}
//Create the ring buffer
player->ringBuffer = new CARingBuffer();
player->ringBuffer->Allocate(player->streamFormat.mChannelsPerFrame,
player->streamFormat.mBytesPerFrame,
bufferSizeFrames * 3);
printf("Number of channels: %d\n", player->streamFormat.mChannelsPerFrame);
printf("Number of buffers: %d\n", player->inputBuffer->mNumberBuffers);
//Set render proc to supply samples
AURenderCallbackStruct callbackStruct;
callbackStruct.inputProc = InputRenderProc;
callbackStruct.inputProcRefCon = player;
status = AudioUnitSetProperty(player->inputUnit,
kAudioOutputUnitProperty_SetInputCallback,
kAudioUnitScope_Global,
0,
&callbackStruct,
sizeof(callbackStruct);
assert(status == noErr);
status = AudioUnitInitialize(player->inputUnit);
assert(status == noErr);
player->firstInputSampleTime = -1;
player->inToOutSampleTimeOffset = -1;
printf("Finished CreateInputUnit()\n");
}
So this is my render callback function where I am accessing the individual buffers. :
OSStatus GraphRenderProc(void * inRefCon,
AudioUnitRenderActionFlags * ioActionFlags,
const AudioTimeStamp * inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList * ioData)
{
MyAUGraphPlayer * player = (MyAUGraphPlayer *) inRefCon;
if(player->firstOutputSampleTime < 0.0) {
player->firstOutputSampleTime = inTimeStamp->mSampleTime;
if((player->firstInputSampleTime > -1.0) &&
(player->inToOutSampleTimeOffset < 0.0)) {
player->inToOutSampleTimeOffset = player->firstInputSampleTime - player->firstOutputSampleTime;
}
}
//Copy samples out of ring buffer
OSStatus outputProcErr = noErr;
outputProcErr = player->ringBuffer->Fetch(ioData,
inNumberFrames,
inTimeStamp->mSampleTime + player->inToOutSampleTimeOffset);
//BUT THIS IS NOT HOW IT IS SUPPOSED TO WORK
Float32 * data = (Float32 *) ioData->mBuffers[0].mData;
Float32 * data2 = (Float32 *) ioData->mBuffers[1].mData;
for(int frame = 0; frame < inNumberFrames; frame++)
{
Float32 sample = data[frame] + data2[frame];
data[frame] = data2[frame] = sample;
}
return outputProcErr;
}
Although your code looks overly complicated for the task it seems to manage, I'll try to answer your question:
There's nothing wrong with your concept of retrieving sample data inside a callback. It would though be insufficient if dealing with multichannel audio devices. How many channels the device has and which is the channel layout, format etc. you query through AudioStreamBasicDescription for given device. This property serves for the initialization of the rest of your processing chain. You allocate audio buffers on initialization, or let the program do it for you (please read documentation).
In case you find more comfortable using extra buffers to copy to just for data crunching and DSP, you can manage it inside your callback like this (simplified code):
Float32 buf[streamFormat.mChanelsPerFrame][inNumberFrames];
for(int ch; ch < streamFormat.mChanelsPerFrame; ch++){
Float32 data = (Float32 *)ioData->mBuffers[ch].mData;
memcpy(buf[ch], data, inNumberFrames*sizeof(Float32));
}

Resources