Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.129.13.201] |
|
Сообщ.
#1
,
|
|
|
Хочу сделать подобное, только на морально устаревающем смартфоне с винмобаил 6.1.
Надо получить циклическую последовательность импульсов 1-3мС с дискретностью 0.1мС и, с возможностью независимого их изменения. Возможно,надо заранее создать 10 байтовых массивов и подключать нужный.Может кто подскажет, как подобное реализовать с минимальной загрузкой цпу. Надеюсь,сама идея кого-нибудь заинтересует, т.к. телефоны имеют камеры ,вай-фай,жпс,акселерометр, и совсей этой можно попытаться взлететь , ну или хотябы поползти... ПС В "железе" я разбираюсь чуть лучше и возможна кооперация. |
Сообщ.
#2
,
|
|
|
Цитата Надо получить циклическую последовательность импульсов 1-3мС с дискретностью 0.1мС Ничего непонятно, но не суть. Протокол обмена можно потом додумать. А вывод звука под винмобайл возможен при помощи всё тех же Waveform Audio Functions Во всяком случае, попробуйте. Начните с waveOutGetNumDevs и waveOutGetDevCaps. Или при помощи PlaySound попробуйте "побибикать" под винмобайл. Если фунции отработают, то и прочими из списка можно воспользоваться. |
Сообщ.
#3
,
|
|
|
Цитата Prince @ Ничего непонятно, но не суть. Протокол обмена можно потом додумать. Чего тут не понять? Стандартный модельный протокол PPM. Каждые 20мС передается посылка , состоящая из длинного импульса 3-4мс (маркер начала)и канальных импульсов . Длительночть последних определяет положение рулей , скорость вращения моторов... 1,5мс -нейтраль , +- до 0,5 от нее -отклонение рулей в ту или другую сторону. как-то так. Я по эксперементировал тут (нашел исходник на басике c WaveOut, подрихтовал ) Результат плачевный. Сами импульсы разной скважности получаются хорошо, но растояние между ними 40мС. Делал я так: Создал несколько 100 байтовых массивов , забил в них 0 и 255, в нужном кол-ве и поочередно их подсовывал. Частота 44100. Это я чего-то не так делаю,бэйсик тормозит или виндоуз с такими маленькими буферами не работает? |
Сообщ.
#4
,
|
|
|
Скорее всего железка требует буфер кратный 4096 отсюда 4096/(2*44100)~46 мс. Можно в таймере попробовать писать изменения в буфере после того как отдали его на обработку, но не факт что взлетит(заработае ).
|
Сообщ.
#5
,
|
|
|
Цитата Делал я так: Создал несколько 100 байтовых массивов , забил в них 0 и 255, в нужном кол-ве и поочередно их подсовывал. Частота 44100. Механизм подсовывания очень важен. Цитата Делал я так: Создал несколько 100 байтовых массивов , забил в них 0 и 255, в нужном кол-ве и поочередно их подсовывал. Частота 44100. Хотя бы 100 мс(а не байт). Тем паче, что девайс маломощный. 100 байт - это ну совсем ничего. Цитата виндоуз с такими маленькими буферами не работает? Винда мноозадачная, 100 байт выплюнутся за 22 мс, нужен следующий буфер, а ваше приложение в отключке, потому что процессорное время уже отдано другому процессу. 100 мс буфер(или больше) и правильный механизм подсовывания - ключ к успеху. Цитата Чего тут не понять? Стандартный модельный протокол PPM. Даже если б я его знал назубок, пот такому описаниюи вряд ли бы догадался. |
Сообщ.
#6
,
|
|
|
Цитата Prince @ 100 байт выплюнутся за 22 мс Я вообще то расчитывал на 2,2мС , и осцил показывает , что так и есть. Цитата Хотя бы 100 мс Это невыносимо долго. Есть мысль удлинить до 20мС -заполнять буфер сразу всей посылкой. Цитата Механизм подсовывания очень важен. Через сообщение главному окну, кажется, я не сильно вникал. В оригинале эта программка делала синус и потрескивала. Думаю, из-за незаконченности синуса в буфере. |
Сообщ.
#7
,
|
|
|
А, да, 2мс. Это даже не ничто, а ничего от ничто.
Цитата Это невыносимо долго. Невыносимо долго ЧТО? Невыносимо долго для чего? Цитата Есть мысль удлинить до 20мС -заполнять буфер сразу всей посылкой. Интуитивно догадываюсь, что вам нужно поработать над алгоримтом формирования сигнала, он не должен в идеале зависеть от длительности буфера. Цитата Через сообщение главному окну, кажется, я не сильно вникал. В оригинале эта программка делала синус и потрескивала. Думаю, из-за незаконченности синуса в буфере. Не, так вы вникните, механизм подсовывания важен вообще, его реализация т.е., а не способ(окно, поток, функция) уведомления только. Сколько в очереди буферов крутится вообще? Сколько в очереди находятся одновременно? Как происходит заполнение буфера, кто является инициатором процесса? |
Сообщ.
#8
,
|
|
|
В идеале, картина должна бы выглядеть так:
в очереди крутятся 2 буфера, длительностью 20 мс каждый, равной длине посылки. когда буфер освобождается, происходит опрос органов управления, заполнение пустого буфера новой посылкой и постановка его в очередь. Тогда время реакции на действия пилота было бы в районе 40 мс. А опрос органов управления и формирование посылки были бы синхронизированы. Но такой короткий буфер, девайс, вероятно, не потянет(но всё же попробуйте). А если длительность буфера больше длительности посылки, пусть даже кратной 20 мс, придётся разделить опрос органов управления и процесс заполнения буфера и озадачиться синхронизацией очереди буферов и очереди управляющих команд. Как такой вариант реализовать, в деталях пока без понятия. Возможно, есть вариант более красивый, но с ходу его не вижу. |
Сообщ.
#9
,
|
|
|
Как оказалось дело не в "бабине", то есть не совсем в ней. Я сначала сделал буфер в 5000байт , и теже ~40мС остались. После чего я посмотрел работу программы этого пограмиста , там щелкают теже 40мс.
Так что , возможно , шансы есть. Я хотел сделать так: создать 2-3 десятка 100 байтных буферов, занести в них образы импульсов разной скважности(больше- меньше разных градаций и нейтраль) , и каждый раз подсовывать нужный буфер соответствующий значению команды текущего канала. Второй вариант : сделать два буфера длиною в полную посылку~20мС ,заранее заполненых нейтральными значениями, и пока один "играет" второй дописывать нужным колвом "0" или "1" в нужных местах . |
Сообщ.
#10
,
|
|
|
В виндоуме работа со зауком это шаманство. Так как не отгадаешь что и как будет работать. Вот второй вариант с 2 буферами возможен. Но я бы лучше взял 3 буфера по 4096. Пока первый ишрается, третий заполняем. Второй должен быть отдаем на исполнение, до того как 1 начнёт играть. Тройной буфер нуден для того чтобы небыло барьера. Т.е. щелчков.
А размер должен быть кратен странице иначе поведение плохо предсказуемо. Про размер буфера насколько помню это оговарено в документации. |
Сообщ.
#11
,
|
|
|
Цитата лучше взял 3 буфера по 4096. 3 буфера будут сильно замедлять реакцию на органы управления.А щелчки , я же не слушать их буду, главное чтобы они не мешали находить среди них правльные импульсы. |
Сообщ.
#12
,
|
|
|
Попробовал играть меандр на Си , получилось чуть лучше, но тоже щелкает.Два буфера по 5000 байт,пробовал увеличить в двое -без изменений.Сделал через CALLBACK_WINDOW
case MM_WOM_DONE: if (numBuff==0) { waveOutWrite(hWaveOut, &whdr1, sizeof(whdr1)); numBuff=1; } else { numBuff=0; waveOutWrite(hWaveOut, &whdr2, sizeof(whdr2)); } return TRUE; Может можно что-то получше? И что быстрее через окно или функцию? В структуре WAVEHDR есть зарезервированное lpNext , наверно указатель на структуру следуущего буфера.Думаю, это то что мне подошло БЫ. Посмотрел осциллом- теже 40мс между переключениями. |
Сообщ.
#13
,
|
|
|
Имхо, что-то не то или не так делаете. Выкладывайте весь кусок от waveinopen с обработкой сообщений. Код, заполняющий буфер, не нужен. Просто камент поставьте "тут записываем в такой-то буфер такие-то данные".
Цитата Цитата if (numBuff==0) { waveOutWrite(hWaveOut, &whdr1, sizeof(whdr1)); numBuff=1; } lParam = (LONG) lpwvhdr. Т.е., адрес заголовка и буфера у вас и так есть. numBuff не нужен, и такой способ идентификации ненадёжен. Цитата Посмотрел осциллом- теже 40мс между переключениями. В смысле, пауза в 40 мс между окончанием одного буфера и началом следущего? Создайте 5 буферов по 0.5 с, заполните их значениями непрерывной синусоиды, и поставьте в очередь. Если разрывов не будет, значит вы накосячили, если разрывы останутся, значит, девайс косячит, но я в этом сомневаюсь. |
Сообщ.
#14
,
|
|
|
Цитата Выкладывайте весь кусок Все целиком. #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <windowsx.h> #include <commctrl.h> #include <tchar.h> #include <mmsystem.h> #include <stdio.h> #include "main.h" #pragma comment(lib, "winmm.lib") #define UNICODE #define NELEMS(a) (sizeof(a) / sizeof((a)[0])) #define nRate 44100 #define nBits 8 #define nBytesPerSample nBits / 8 #define nBytes 10000 // /** Prototypes **************************************************************/ static INT_PTR CALLBACK MainDlgProc(HWND, UINT, WPARAM, LPARAM); /** Global variables ********************************************************/ static HANDLE ghInstance; HWAVEOUT hWaveOut; WAVEFORMATEX wfx; // для получения описателя звукового устройства: WAVEHDR whdr1; WAVEHDR whdr2; static UCHAR Buffer1[nBytes]; static UCHAR Buffer2[nBytes]; UCHAR numBuff ; /**************************************************************************** * * * Function: WinMain * * * * Purpose : Initialize the application. Register a window class, * * create and display the main window and enter the * * message loop. * * * * History : Date Reason * * 00/00/00 Created * * * ****************************************************************************/ int PASCAL WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow) { INITCOMMONCONTROLSEX icc; WNDCLASSEX wcx; ghInstance = hInstance; icc.dwSize = sizeof(icc); icc.dwICC = ICC_WIN95_CLASSES /*|ICC_COOL_CLASSES|ICC_DATE_CLASSES|ICC_PAGESCROLLER_CLASS|ICC_USEREX_CLASSES|... */; InitCommonControlsEx(&icc); wcx.cbSize = sizeof(wcx); if (!GetClassInfoEx(NULL, MAKEINTRESOURCE(32770), &wcx)) return 0; wcx.hInstance = hInstance; wcx.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDR_ICO_MAIN)); wcx.lpszClassName = _T("ppmClass"); if (!RegisterClassEx(&wcx)) return 0; /* The user interface is a modal dialog box */ return DialogBox(hInstance, MAKEINTRESOURCE(DLG_MAIN), NULL, (DLGPROC)MainDlgProc); } /**************************************************************************** * * * Function: MainDlgProc * * * * Purpose : Process messages for the Main dialog. * * * * History : Date Reason * * 00/00/00 Created * * * ****************************************************************************/ static INT_PTR CALLBACK MainDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_INITDIALOG: /* * TODO: Add code to initialize the dialog. */ for (UINT i = 0; i < nBytes; i++) { if((i/100)%2) Buffer1[i] = 255; else Buffer1[i] = 0; } for (UINT i = 0; i < nBytes; i++) { if((i/10)%2) Buffer2[i] = 255; else Buffer2[i] = 0; } wfx.wFormatTag = WAVE_FORMAT_PCM; // wav-формат wfx.nChannels = 1; // моно - звук wfx.nSamplesPerSec = nRate; // 44100 Гц wfx.nAvgBytesPerSec = nBytesPerSample * nRate; // байт в секунду wfx.nBlockAlign = nBytesPerSample; wfx.wBitsPerSample = nBits; wfx.cbSize = 0; // For only WAVE_FORMAT_PCM formats, this member is ignored numBuff=0; return TRUE; case WM_SIZE: /* * TODO: Add code to process resizing, when needed. */ return TRUE; case MM_WOM_DONE: if (numBuff==0) { waveOutWrite(hWaveOut, &whdr1, sizeof(whdr1)); numBuff=1; } else { numBuff=0; waveOutWrite(hWaveOut, &whdr2, sizeof(whdr2)); } return TRUE; case WM_COMMAND: switch (GET_WM_COMMAND_ID(wParam, lParam)) { /* * TODO: Add more control ID's, when needed. */ case IDSTART: waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, (UINT)hwndDlg, 0L, CALLBACK_WINDOW); whdr1.lpData = (LPSTR)Buffer1; whdr1.dwBufferLength = nBytes; // размер буфера в байтах. whdr1.dwFlags = 0; // Буфер подготовлен (зафиксирован в памяти) whdr1.dwLoops = 0; // без циклов, поле должно быть нулевым. whdr2.lpData = (LPSTR)Buffer2; whdr2.dwBufferLength = nBytes; // размер буфера в байтах. whdr2.dwFlags = 0; // Буфер подготовлен (зафиксирован в памяти) whdr2.dwLoops = 0; // без циклов, поле должно быть нулевым. waveOutPrepareHeader(hWaveOut, &whdr1, sizeof(WAVEHDR)); waveOutPrepareHeader(hWaveOut, &whdr2, sizeof(WAVEHDR)); waveOutWrite(hWaveOut, &whdr1, sizeof(whdr1)); return TRUE; case IDOK: EndDialog(hwndDlg, TRUE); waveOutUnprepareHeader (hWaveOut, &whdr1, sizeof(WAVEHDR)); waveOutUnprepareHeader (hWaveOut, &whdr2, sizeof(WAVEHDR)); waveOutReset (hWaveOut); waveOutClose (hWaveOut); return TRUE; } break; case WM_CLOSE: EndDialog(hwndDlg, 0); return TRUE; /* * TODO: Add more messages, when needed. */ } return FALSE; } Цитата numBuff не нужен, и такой способ идентификации ненадёжен. Это для того чтобы решить , какой буфер играть. Цитата В смысле, пауза в 40 мс между окончанием одного буфера и началом следущего? Да. Цитата Создайте 5 буферов по 0.5 с Не думаю, что в этом есть смысл , я нашел здесь ,неподалеку , программку на ВБ . Она прекрасно играет без разрывов. У нее два буфера по 44100(1 сек) . Я помучил ее , она не трещит, если уменьшать до 1800 байт. Кстати, это ~40мС. |
Сообщ.
#15
,
|
|
|
Очередь буферов устроена по принципу FIFO.
Сначала нужно поставить в очередь все(несколько) буферы, при помощи wavoutwrite. Когда первый в очереди буфер воспроизведен, он выталкивается из очереди; начинает воспроизводиться следующий за ним, без задержки(теперь этот буфер становится первым в очереди). Когда буфер вытолкнут из очереди, приложению посылается месседж MM_WOM_DONE, поле lparam которого содержит адрес заголовка буфера. А заголовок, в свою очередь, хранит адрес буфера в lpdata. Его(буфер) можно заполнить новой порцией данных и снова поставить в очередь(в конец очереди) при помощи wavoutwrite. case IDSTART: waveOutOpen(&hWaveOut, WAVE_MAPPER, &wfx, (UINT)hwndDlg, 0L, CALLBACK_WINDOW); //... waveOutPrepareHeader(hWaveOut, &whdr1, sizeof(WAVEHDR)); waveOutPrepareHeader(hWaveOut, &whdr2, sizeof(WAVEHDR)); waveOutWrite(hWaveOut, &whdr1, sizeof(whdr1)); //сюда добавить WaveOutWrite(hWaveOut, &whdr2, sizeof(whdr2)); return TRUE; Цитата Это для того чтобы решить , какой буфер играть. См. выше. Какая-то специальная нумерация буферов не нужна(когда-то я тоже такие костыли изобретал). Точные "координаты" буфера в lparam обрабатываемого месседжа MM_WOM_DONE. MM_WOM_DONE не указывает "какой буфер играть", а сообщает о том, что "такой-то буфер уже отыграл, дальше делайте с ним, что хотите". WaveOutWrite не "начинает играть такой-то буфер", а ставит буфер в конец очереди [буферов]. Поставленный в очередь буфер начинает воспроизводиться сразу только в том случае, если очередь была пуста, и воспроизведение не остановлено при помощи WaveOutPause. |
Сообщ.
#16
,
|
|
|
Спасибо!
waveOutWrite(hWaveOut, &whdr1, sizeof(whdr1)); //сюда добавить WaveOutWrite(hWaveOut, &whdr2, sizeof(whdr2)); Это сильно помогло! Цитата Очередь буферов устроена по принципу FIFO. .... WaveOutWrite не "начинает играть такой-то буфер", а ставит буфер в конец очереди А это очень интересно. |
Сообщ.
#17
,
|
|
|
Цитата Точные "координаты" буфера в lparam обрабатываемого месседжа MM_WOM_DONE. А доставать lparam как-то заморочисто надо? Что то у меня не получается. |
Сообщ.
#18
,
|
|
|
static INT_PTR CALLBACK MainDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
Как оно там в си может выглядеть, я не в курсе. А вообще, вытягиваение координат из lparam и доступ к элементам буфера(семплам) выглядит(может выглядеть) так: var pb:pbyte:// указатель на байт, который будем использовать для доступа к содержимому буфера; //устанавливаем указатель на начало буфера(допустим, буфер 8 битный, один канал): pb:=pbyte(pwavehdr(lparam).lpdata); // приведение типов, сначала lparam к wavehdr, затем lpdata к pbyte; // дальше чё-то делаем с ним: for i:=0 to pwavehdr(lparam).dwBufferLength-1 do Begin pb:=128;// заполняем текущий семпл "тишиной" inc(pb)// переходим к следующему семплу end; |
Сообщ.
#19
,
|
|
|
"Семен Семеныч"(с) . Это я тормозил, надо заправлять тот же буфер который только что отыграл...
case MM_WOM_DONE: waveOutWrite(hWaveOut,(PWAVEHDR) lParam, sizeof(WAVEHDR)); |
Сообщ.
#20
,
|
|
|
И предварительно заправлять несколько буферов в очередь. И позаботиться о том, чтобы после отыгрывания буфера, он был возвращен в очередь до того, как отыграет следующий за ним. Этот пункт накладывает ограничения на минимальный размер буфера.
Цитата я нашел здесь ,неподалеку , программку на ВБ . Она прекрасно играет без разрывов. У нее два буфера по 44100(1 сек) . Я помучил ее , она не трещит, если уменьшать до 1800 байт. Кстати, это ~40мС Постепенно уменьшая размер буфера, всё больше и больше нагружаем проц и систему, сообщения MM_WOM_DONE сыпятся всё чаще и чаще. Уменьшаем, уменьшаем дальше, и опа, в какой-то момент, приложение не успевает обработать очередной MM_WOM_DONE, и добавить новый буфер в очередь до окончания воспроизведения текущего. Звук рвётся. Этот момент(и минимальный размер буфера, при котором разрывов не будет) зависит от кванта времени, выделяемого приложению виндой, производительности системы и загруженности её работающими параллельно другими процессами. Почему я говорил про 100 мс. С буфером такой длительности приложение в штатной ситуации никогда не порвёт звук и не сильно нагружает систему. На "больших" компах. |
Сообщ.
#21
,
|
|
|
Снова реанимировался вопрос про то , как узнать какой буфер только что отыграл.
Цитата Это из интернета.Параметр lParam содержит адрес структуры WAVEHDR, соответствующей проигранному блоку. Действительно содержит, только есть небольшое отличие с тем что я отправляю. Вскрытие показало, что у обоих младшие 64р-да совпадают, но у lParam старшие 64 равны 0, а у адреса отправленной структуры в младшем -1. Можно ее , конечно, придушить, но... |
Сообщ.
#22
,
|
|
|
В винмобайл? Вот прям 64 бита? Да ещё и старшие/младшие?
Цитата младшие 64р-да совпадают, но у lParam старшие 64 равны 0, а у адреса отправленной структуры в младшем -1. Ничего непонятно. |
Сообщ.
#23
,
|
|
|
Цитата Prince @ В винмобайл? Нет. 8.1 (64) |
Сообщ.
#24
,
|
|
|
Цитата младшие 64р-да совпадают, но у lParam старшие 64 равны 0, а у адреса отправленной структуры в младшем -1. Осталось эту странную лексическую конструкцию осилить. |
Сообщ.
#25
,
|
|
|
Не надо ее осиливать, это какие-то проблемы с 64.
А у меня проблема я не знаю как сравнить : lParam с &whdr(структура WAVEHDR) Ругается, что я сравниваю не то не с тем. |