Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.222.147.4] |
|
Страницы: (2) [1] 2 все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
Начал изучение работы с ASIO. Взял простой пример с сайта разработчика BassAsio, немного изменил (проект прикрепил). Программа работает даже на простом ноутбуке после установки ASIO4ALL, показывает наличие микрофона и динамиков. Однако, хотя в проекте установлена Многопоточная (/MT) библиотека времени выполнения, программа работает только тогда, когда рядом (или в указанной в path директории) находится bassasio.dll, в противном случае возникают ошибки. Подскажите, пожалуйста, можно ли сделать так, чтобы dll не требовалась.
Прикреплённый файлBassAsio.zip (19,81 Кбайт, скачиваний: 251) |
Сообщ.
#2
,
|
|
|
Решил попробовать поработать с BassAsio на своей Дельте 66. За основу взял прикрепленный проект BassAsio. Информацию о входах Дельты 66 получил следующую:
M-Audio ASIO 5078 Количество входов: 8 Размеры буферов (min, max, default) (количество отсчетов): 64 4096 256 Устройство инициализировано --------------------- Входные устройства ASIO ---------------------- 0 Analog In 1 Delta-66 1 Analog In 2 Delta-66 2 Analog In 3 Delta-66 3 Analog In 4 Delta-66 4 SPDIF In L Delta-66 5 SPDIF In R Delta-66 6 Monitor L Delta-66 7 Monitor R Delta-66 Попробовал записать звук пока только для одного канала (канал 0), используя пример из bassasio13.zip: #include <windows.h> #include <conio.h> #include <string> #include <vector> #include <iostream> #include "bassasio.h" #pragma comment(lib, "bassasio.lib") #define BUFSTEP 200000 // memory allocation unit int input=0; // current input source char *recbuf=NULL; // recording buffer DWORD reclen; // recording length BOOL recording=FALSE; float level=0; int nKan; std::vector<FILE*> pf; void ErrorMes(std::string mes, int Res); void GetAsioInfoCyr(void); void RecordAsio(); int main(int argc, char* argv[]) { setlocale(LC_ALL, ""); GetAsioInfoCyr(); std::vector<std::string> vFileName; vFileName.push_back("file1"); nKan = 1; for(int j = 0; j < nKan; j++) { pf.push_back(fopen((char*) (vFileName[j] + ".pcm").c_str(), "wb")); } RecordAsio(); for(int j = 0; j < nKan; j++) { fclose(pf[j]); } system("pause"); } void ErrorMes(std::string sMes, int Res) { std::cout << sMes << Res << std::endl; system("pause"); exit(EXIT_SUCCESS); // EXIT_SUCCESS, EXIT_FAILURE } void GetAsioInfoCyr(void) { int i; if (!BASS_ASIO_Init(0, BASS_ASIO_THREAD)) { ErrorMes("Не удалось инициализировать устройство. Ошибка: ", BASS_ASIO_ErrorGetCode()); } BASS_ASIO_INFO di; BASS_ASIO_GetInfo(&di); std::cout << di.name << "\t" << di.version << std::endl; std::cout << "Количество входов: " << di.inputs << std::endl; std::cout << "Размеры буферов (min, max, default) (количество отсчетов): " << di.bufmin << "\t" << di.bufmax << "\t" << di.bufpref << std::endl; if(di.initflags == 1) std::cout << "Устройство инициализировано" << std::endl; else std::cout << "Устройство неинициализировано" << std::endl; BASS_ASIO_CHANNELINFO chi; std::cout << "\n--------------------- Входные устройства ASIO ----------------------\n" << std::endl; for (i = 0; BASS_ASIO_ChannelGetInfo(TRUE, i, &chi); i++) { std::cout << i << "\t" << chi.name << std::endl; } std::cout << std::endl; BASS_ASIO_Free(); } DWORD CALLBACK AsioProc(BOOL isinput, DWORD channel, void *buffer, DWORD length, void *user) { if (!recbuf) return 0; // increase buffer size if needed if ((reclen%BUFSTEP)+length>=BUFSTEP) { recbuf= (char*) realloc(recbuf,((reclen+length)/BUFSTEP+1)*BUFSTEP); if (!recbuf) { ErrorMes("Не удалось выделить буфер", 1); return 0; } } // buffer the data memcpy(recbuf+reclen,buffer,length); reclen+=length; while(recording == TRUE) fwrite(buffer, length, 1, pf[0]); return 0; } void RecordAsio() { if (!BASS_ASIO_Init(0, BASS_ASIO_THREAD)) { ErrorMes("Не удалось инициализировать устройство. Ошибка: ", BASS_ASIO_ErrorGetCode()); } WAVEFORMATEX wvFormat; wvFormat.wFormatTag = WAVE_FORMAT_PCM; wvFormat.nChannels = 1; wvFormat.nSamplesPerSec = 44100; wvFormat.wBitsPerSample = 16; wvFormat.nBlockAlign = (wvFormat.nChannels*wvFormat.wBitsPerSample + 7)/8; wvFormat.nAvgBytesPerSec = wvFormat.nSamplesPerSec*wvFormat.nBlockAlign; wvFormat.cbSize = sizeof(wvFormat); if (recbuf) { // free old recording BASS_ASIO_Stop(); // stop ASIO device in case it was playing BASS_ASIO_ChannelReset(FALSE,-1,BASS_ASIO_RESET_ENABLE); // disable outputs in preparation for recording free(recbuf); recbuf=NULL; } // allocate initial buffer and make space for WAVE header recbuf=(char*)malloc(BUFSTEP); reclen=44; BASS_ASIO_ChannelReset(TRUE,-1,BASS_ASIO_RESET_ENABLE); // disable all inputs, then... BASS_ASIO_ChannelEnable(TRUE, 0, AsioProc,0); // enable the selected BASS_ASIO_ChannelSetFormat(TRUE, 0, BASS_ASIO_FORMAT_16BIT); // want 16-bit data if (!BASS_ASIO_Start(0,0)) { ErrorMes("Can't start recording", BASS_ASIO_ErrorGetCode()); free(recbuf); recbuf=0; return; } recording=TRUE; _getch(); recording=FALSE; BASS_ASIO_Free(); } - как работает в Asio колбек функция; - чем отличаются друг от друга buffer и recbuf, length от length и др.; - как правильно добавлять свободные буферы для записи; - как колбек функция информирует о заполнении буфера; - как правильно прекратить запись; - как правильно освобождать буферы после окончанию записи. Помогите, пожалуйста, разобраться. Проект прикрепил. |
Сообщ.
#3
,
|
|
|
Забыл прикрепить файл
Прикреплённый файлBassAsio.zip (21,05 Кбайт, скачиваний: 237) |
Сообщ.
#4
,
|
|
|
Цитата как работает в Asio колбек функция Как и любая колбек функция. Адрес функции в качестве параметра передаётся в ASIO_ChannelEnable, а потом эта функция вызывается откуда-то из недр bassasio или ещё откуда-то. Цитата как правильно добавлять свободные буферы для записи Механизм выделения/освобождения памяти спрятан внутри bassasio, ничего добавлять не нужно, можно указать желательный размер буфера при инициализации(или использовать размер по умолчанию). Цитата как колбек функция информирует о заполнении буфера Сам по себе вызов функции - это и есть механизм уведомления приложения о том, что в канале записи есть данные для обработки. Адрес и размер данных передаются в качестве параметров. Цитата Звук с микрофона, подключенного к первому входному каналу, не записывается Активировать 1-й канал через ASIO_ChannelEnable и записывать данные канала в файл, в колбек. Только в другой файл, не в тот, куда сливаются данные 0-го канала. Будут 2 моно файла. Если необходим стерео файл, каналы объединяются посредством BASS_ASIO_ChannelJoin, тогда семплы каналов будут чередоваться в буфере. Цитата как правильно прекратить запись; BASS_ASIO_Stop BASS_ASIO_ChannelEnable BASS_ASIO_ChannelReset BASS_ASIO_ChannelPause BASS_ASIO_Free Смотря что подразумевается под "прекратить". Читайте справку. |
Сообщ.
#5
,
|
|
|
Спасибо большое за ответы! Возникли ещё вопросы:
- как задать размер буфера (length) в колбэк функции? - можно ли в азио изменять усиление по входу? Интересует максимальное усиление. |
Сообщ.
#6
,
|
|
|
bassasio.dll - это библиотека-обёртка над API ASIO, спрятанными в других dll, которые устанавливаются с драйвером устройства(являются его частью). Так же как библиотека bass.dll - это обёртка над API DirectSound(+ кучка дополнительных плюшек).
Bass, bassasio упрощают работу с низкоуровневыми интерфейсами. Множество действий, выполняемых програмистом при работе с низкоуровневыми функциями, они берут на себя. В частности, вам не нужно выделять/освобождать память под буферы для записи. Все операции с буферами скрыты внутри bassasio. Размер буфера(в семплах) задаётся при старте устройства в BASS_ASIO_Start. От размера буфера, и количества задействованых каналов, будет зависеть частота вызова колбэк, соотвественно и нагрузка на проц(систему). Цитата как задать размер буфера (length) в колбэк функции И ещё раз. length не нужно задавать. Вы пишете колбэк функцию предопределенного формата и передаете адрес этой функции куда-то "вовнутрь" bassasio при вызове BASS_ASIO_ChannelEnable. Затем, после того, как устройство стартует, кто-то, откуда-то(это неважно), периодически вызывает написанный вами колбэк-код(функцию) и передаёт туда адрес и размер доступных для обработки данных(buffer, length). Ваш код эти данные обрабатывает(ну, вы что-то делаете с этими данными). Или ничего не делаете - дело ваше. Всё. Ничего задавать не нужно. Только обработать этот блок данных(записать в файл, например). Цитата можно ли в азио изменять усиление по входу? Нет. С этим не к ASIO. Хотя библиотека bassasio позволяет изменять уровень сигнала в канале при помощи функции BASS_ASIO_ChannelSetVolume. Но, функция не имеет отношения к настройкам железа. Она изменяет значения семплов, т.е., производит манипуляцию над данными, поступающими от устройства(при записи) или к устройству(при воспроизведении). В MME есть функции для управления аппаратным микшером устройства. Функции группы MixerXXX. |
Сообщ.
#7
,
|
|
|
Спасибо за подробные объяснения.
При определении параметров устройства я получил несколько значений буфера: Размеры буферов (min, max, default) (количество отсчетов): 64 4096 256 В связи с этим я подумал, что можно задавать значение буфера. Хотел бы уточнить следующее: 1. ASIO в принципе не позволяет управлять входным аппаратным усилением (в отличие от MME) или это не умеет делать bassasio (в обертке это не предусмотрено)? 2. Если я в результате экспериментов вижу, что заданное по умолчанию значение буфера не подходит (сейчас у меня 512), как же я могу снизить нагрузку на процессор, частоту вызова колбэк функции и др. |
Сообщ.
#8
,
|
|
|
Проверил возможность изменения размера буфера в Start. Все работает. Вопрос 2 снимаю как глупый. Тем более, что Вы уже это объяснили.
|
Сообщ.
#9
,
|
|
|
Цитата ASIO в принципе не позволяет управлять входным аппаратным усилением По моим сведениям - нет. ASIO Interface Specification v 2.0 |
Сообщ.
#10
,
|
|
|
Спасибо за ответы!
|
Сообщ.
#11
,
|
|
|
Попробовал определить программно некоторые допустимые параметры Delta 66:
#include <string> #include <iostream> #include "bassasio.h" #pragma comment(lib, "bassasio.lib") using namespace std; void GetAsioInfo(void); void ErrorMes(string sMes, int Res); int main(int argc, char* argv[]) { setlocale(LC_ALL, ""); GetAsioInfo(); system("pause"); return(EXIT_SUCCESS); } void ErrorMes(string sMes, int Res) { cout << sMes << Res << endl; system("pause"); exit(EXIT_SUCCESS); } void GetAsioInfo(void) { if (!BASS_ASIO_Init(0, BASS_ASIO_THREAD)) ErrorMes("Не удалось инициализировать устройство. Ошибка: ", BASS_ASIO_ErrorGetCode()); int nSamplesPerSec[] = {44100, 48000, 64000, 88200, 96000, 192000, 1600000}; cout << "\n---------- Определение поддерживаемой дискретизации ------------\n" << endl; for(int i = 0; i < sizeof(nSamplesPerSec)/sizeof(int); i++) { if(BASS_ASIO_ChannelSetRate(TRUE, 0, (double) nSamplesPerSec[i]) == FALSE) { cout << "\n!!! Ошибка при установке дискретизации " << nSamplesPerSec[i] << endl << endl; break; } cout << "Установлена дискретизация " << BASS_ASIO_ChannelGetRate(TRUE, 0) << endl; } cout << "\n---------- Определение поддерживаемой разрядности ------------\n" << endl; int nBitsPerSample[] = {BASS_ASIO_FORMAT_16BIT, BASS_ASIO_FORMAT_24BIT, BASS_ASIO_FORMAT_32BIT, BASS_ASIO_FORMAT_FLOAT, BASS_ASIO_FORMAT_DSD_LSB, BASS_ASIO_FORMAT_DSD_MSB}; for(int i = 0; i < sizeof(nBitsPerSample)/sizeof(int); i++) { if(BASS_ASIO_ChannelSetFormat(TRUE, 0, nBitsPerSample[i]) == FALSE) { cout << "\n !!! Ошибка при установке разрядности: элемент массива nBitsPerSample номер " << i << endl << endl; break; } cout << "Установлена дискретизация " << BASS_ASIO_ChannelGetFormat(TRUE, 0) << endl; } cout << "\n---------- Определение поддерживаемого усиления -------------\n" << endl; float volume = 1.0e32; if(BASS_ASIO_ChannelSetVolume(TRUE, 0, volume) == FALSE) cout << "\n !!! Ошибка при установке усиления: " << volume << endl << endl; cout << "Установлено усиления: " << BASS_ASIO_ChannelGetVolume(TRUE, 0) << endl << endl; cout << "\n---------- Определение размера буфера -------------\n" << endl; int nBuf = 8192; if(BASS_ASIO_Start(nBuf, 0) == FALSE) cout << "\n !!! Ошибка при установке размера буфера: " << nBuf << endl << endl; BASS_ASIO_INFO di; BASS_ASIO_GetInfo(&di); cout << "Установлен размер буфера: " << di.bufpref << endl << endl; BASS_ASIO_Free(); } ---------- Определение поддерживаемой дискретизации ------------ Установлена дискретизация 44100 Установлена дискретизация 48000 Установлена дискретизация 64000 Установлена дискретизация 88200 Установлена дискретизация 96000 Установлена дискретизация 192000 Установлена дискретизация 1.6e+006 ---------- Определение поддерживаемой разрядности ------------ Установлена дискретизация 16 Установлена дискретизация 17 Установлена дискретизация 18 Установлена дискретизация 19 !!! Ошибка при установке разрядности: элемент массива nBitsPerSample номер 4 ---------- Определение поддерживаемого усиления ------------- Установлено усиления: 1e+032 ---------- Определение размера буфера ------------- !!! Ошибка при установке размера буфера: 8192 Установлен размер буфера: 4096 Получается: - BASS_ASIO_ChannelSetRate ошибки не дает (в отличие от BASS_ASIO_ChannelSetFormat и установки размера буфера) и дискретизацию можно задавать любую; - BASS_ASIO_ChannelSetVolume ошибки не дает даже при очень большом значении усиления. В отношении усиления: я попробовал записать сигнал с первого входа при усилении 100. В файл записывается действительно усиленный сигнал. Вы объяснили, что это программное усиление, а не аппаратное. Но какое-то ограничение на усиление ведь должно же быть? В отношении частоты дискретизации вообще непонятно, почему нет срабатывания функции ее установки при безумном значении частоты. Почему такое происходит? |
Сообщ.
#12
,
|
|
|
Для проверки поддерживаемых частот дискретизации устройства есть функция BASS_ASIO_CheckRate.
Для установки частоты дискретизации устройства есть функция BASS_ASIO_SetRate. Ну и загляните в настройки драйвера, какая частота установлена(если установлена) там. Если устройство не поддерживает желаемую частоту, но вы очень хотите именно такую, тогда вы устанавливаете корректную нативную частоту в BASS_ASIO_SetRate и используете BASS_ASIO_ChannelSetRate для установки желаемой(для конкретного канала). При таком раскладе, bassasio будет выполнять передискретизацию данных из нативной частоты устройства в установленную для этого канала, "на лету". Такая фишка может быть полезна, но сведёт на нет одно из преимуществ ASIO, а именно возможность получать данные от устройства "как есть", без промежуточных преобразований(как в системном микшере виндовс). И, возможность отдавать устройству данные в его родном формате, также без промежуточных преобразований. За что ASIO "уважают" аудиофилы. Передискретизация также дополнительно нагружает систему. Для проверки нативных форматов(формата) семпла - BASS_ASIO_ChannelGetInfo. Аналогично, если желаемый формат отличается от нативного, и вы его устанавливаете в BASS_ASIO_ChannelSetFormat, bassasio выполняет преобразование формата и отдаёт вам уже не совсем то, что прилетает от устройства. Всё есть в справке. Эта инфа ни к чему не обязывает, думайте сами, решайте сами. Правильный выбор частоты/формата - субъективен. Если вам не важна "точность передачи", а только "многоканальность" ASIO, можно устанавливать любые удобные частоту дискретизации и формат семпла(естественно, поддерживаемые bassasio). Цитата Но какое-то ограничение на усиление ведь должно же быть Ну, конечно. Допустим, формат 16 бит, а значение семпла 1000. Умножив его на 100, получите 100000, т.е., переполнение 16 битного числа, и как результат - искажение звука. Кроме того, при таком програмном "усилении", вы усиливаете шум аудиотракта, вместе с полезным сигналом. |
Сообщ.
#13
,
|
|
|
Спасибо большое, все понял. Попробую BASS_ASIO_CheckRate и BASS_ASIO_SetRate, надеюсь, они дадут правильные результаты.
|
Сообщ.
#14
,
|
|
|
На всякий пожарный, BASS_ASIO_GetRate возвращает текущую частоту дискретизации, заданную в настройках драйвера вручную, или через BASS_ASIO_SetRate.
|
Сообщ.
#15
,
|
|
|
Спасибо за дополнения
|