Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[54.226.222.183] |
|
Сообщ.
#1
,
|
|
|
Начал изучение работы с ASIO. Взял простой пример с сайта разработчика BassAsio, немного изменил (проект прикрепил). Программа работает даже на простом ноутбуке после установки ASIO4ALL, показывает наличие микрофона и динамиков. Однако, хотя в проекте установлена Многопоточная (/MT) библиотека времени выполнения, программа работает только тогда, когда рядом (или в указанной в path директории) находится bassasio.dll, в противном случае возникают ошибки. Подскажите, пожалуйста, можно ли сделать так, чтобы dll не требовалась.
Прикреплённый файлBassAsio.zip (19,81 Кбайт, скачиваний: 245) |
Сообщ.
#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 Кбайт, скачиваний: 233) |
Сообщ.
#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
,
|
|
|
Спасибо за дополнения
|
Сообщ.
#16
,
|
|
|
Попробовал использовать BassAsio со своей Motu Traveler. Сделал простую программу для установки различной частоты дискретизации:
#include <iostream> #include "bassasio.h" #pragma comment(lib, "bassasio.lib") void ErrorMes(char* sMes, int Res) { std::cout << "\n" << std::cout << sMes << Res << "\n\n"; system("pause"); exit(EXIT_FAILURE); // EXIT_SUCCESS, EXIT_FAILURE } void TestAsio(void) { std::cout << "\n-------------- Определение поддерживаемой частоты дискретизации ------------\n"; int nSamplesPerSec[] = {44100, 48000, 64000, 88200, 96000, 176400, 192000}; if(FALSE == BASS_ASIO_Init(0, BASS_ASIO_THREAD)) ErrorMes("Не удалось инициализировать устройство (или драйвер) командой BASS_ASIO_Init. Ошибка: ", BASS_ASIO_ErrorGetCode()); if(FALSE == BASS_ASIO_SetDevice(0)) ErrorMes("Не удалось выбрать устройство командой BASS_ASIO_SetDevice. Ошибка: ", BASS_ASIO_ErrorGetCode()); if(FALSE == BASS_ASIO_ChannelReset(TRUE,-1,BASS_ASIO_RESET_ENABLE)) ErrorMes("Не удалось сбросить все каналы командой BASS_ASIO_ChannelReset. Ошибка: ", BASS_ASIO_ErrorGetCode()); if(FALSE == BASS_ASIO_ChannelReset(TRUE,-1,BASS_ASIO_RESET_RATE)) ErrorMes("Не удалось установить частоту дискретизации устройства командой BASS_ASIO_ChannelReset c BASS_ASIO_RESET_RATE. Ошибка: ", BASS_ASIO_ErrorGetCode()); for(int i = 0; i < sizeof(nSamplesPerSec)/sizeof(nSamplesPerSec[0]); i++) { if(FALSE == BASS_ASIO_SetRate((double) nSamplesPerSec[i])) { std::cout << "\n!!! Ошибка при установке частоты дискретизации " << nSamplesPerSec[i] << std::endl << std::endl; continue; } std::cout << "\n+++++++++++++++ Установлена частота дискретизация: " << BASS_ASIO_GetRate() << " +++++++++++++++\n\n"; system("pause"); } BASS_ASIO_Free(); } int main(int argc, char* argv[]) { setlocale(LC_ALL, ""); TestAsio(); system("pause"); } Подскажите, пожалуйста, что я делаю неправильно. Проект прикрепил. Прикреплённый файлTestAsio.zip (20,09 Кбайт, скачиваний: 182) |
Сообщ.
#17
,
|
|
|
tuchin, попробуйте, пожалуйста, тестовую программу на своей Дельте
|
Сообщ.
#18
,
|
|
|
Я попробовал. Сообщение об успешной установке: 44100, 48000, 88200, 96000. Ошибки: 64000, 19200. К сожалению, на Delta 66 нет индикаторов, поэтому проверить, правильно ли устанавливается частота дискретизации, можно лишь записав тестовый аудио сигнал в файлы при разных частотах и загрузив их в аудиоредактор. Если Вы дополните свою тестовую программу возможностью записи в файлы при разных частотах хотя бы по одному каналу, обещаю проверить, как только будет время.
|
Сообщ.
#19
,
|
|
|
Запись на Дельте прекрасно работает, все команды установки частоты дискретизации отрабатываются без проблем. Видимо, Моту имеет какие-то особенности, в которых нужно разбираться. Если разберетесь, сообщите, пожалуйста, в чем было дело. Интересно, из-за чего могут быть такие проблемы.
|
Сообщ.
#20
,
|
|
|
Цитата BASS_ASIO_GetRate() рапортует, что частоты устанавливаются успешно. Однако на устройстве есть индикаторы, показывающие текущую частоту дискретизации. Исходя из их показаний частоты дискретизации не переключаются а если после BASS_ASIO_SetRate вызвать BASS_ASIO_ControlPanel и глянуть там, установилась ли заданная частота. |
Сообщ.
#21
,
|
|
|
Я попробовал, не устанавливается. Пробовал и вызывать Start. Если устанавливать частоту через панель и ту же частоту использовать в программе, то программа работает нормально: файлы записываются правильно.Если выбранная в программе частота не соответствует текущей частоте устройства, получаются файлы с нулевым размером
|
Сообщ.
#22
,
|
|
|
Если устанавливать частоту через панель, то нет смысла устанавливать частоту в программе. Она уже и так установлена.
Цитата BASS_ASIO_GetRate() рапортует, что частоты устанавливаются успешно. Однако на устройстве есть индикаторы, показывающие текущую частоту дискретизации. Исходя из их показаний частоты дискретизации не переключаются Глюк. Ваш кэп. Никто не запрещает уставливать частоту через контрол панель. |
Сообщ.
#23
,
|
|
|
А что такое "Ваш кэп"?
|