Последние записи
- Писать/читать файл блоками или целиком (BlockWrite, BlockRead)?
- Посимвольное чтение из файла. Посимвольная запись в файл
- Разделить строку с сохранением разделителя
- Свой прогресс в консоли при выполнении команды ffmpeg
- HttpWebRequest выдаёт много ошибок при неудачном соединении
- Как обратиться к последней ячейке не связанного диапазона
- Управление громкостю MediaPlayer
- Как использовать java для бэкенда веб-сайтов и веб-скриптов?
- Некорректная работа гиперссылок Microsoft Word 2010
- Функция рисования для печати на принтере ScanLine
Интенсив по Python: Работа с API и фреймворками 24-26 ИЮНЯ 2022. Знаете Python, но хотите расширить свои навыки?
Slurm подготовили для вас особенный продукт! Оставить заявку по ссылке - https://slurm.club/3MeqNEk
Online-курс Java с оплатой после трудоустройства. Каждый выпускник получает предложение о работе
И зарплату на 30% выше ожидаемой, подробнее на сайте академии, ссылка - ttps://clck.ru/fCrQw
23rd
Июн
Создание спектрограммы в Дельфи
Многие начинающие программисты пробуют свои силы в создании аудиоплеера. И вот когда у них более-менее получается создать функциональную основную часть и музыка уже играет, хочется добавить еще чего-нибудь крутого. Часто это спектроанализатор или спектрограмма, которую многие ошибочно называют эквалайзером (эквалайзер – это средство настройки, а не анализа звука) [1].
Александр Терлецкий
В этом уроке мы попробуем создать спектрограмму на Delphi, а прикручивать ее мы будем к движку BASS. Скажу сразу, что существует Delphi оболочка для BASS от нашего корейского коллеги под названием TBassPlayer, в которой есть спектрограмма, но наша задача – научиться самим, а не использовать готовые решения. Однако вам никто не запрещает просмотреть исходный код этого компонента (он свободный), это никогда не повредит. Изучив этот урок, вы научитесь создавать свою собственную, уникальную своим внешним видом спектрограмму. Желательно, чтобы вы имели основные понятия об ООП, так как я не буду подробно останавливаться на этом и больше внимания уделю вопросам вывода графики.
Урок разбит на две части. В первой – пишем класс спектрограммы и отлаживаем его, во второй – прикручиваем его к звуковому движку BASS (вы можете использовать и другой движок, FMOD например, но вам придется разобраться самостоятельно как получить уровни частот). Вариант с самостоятельным разбиением спектра с помощью преобразования Фурье, без использования каких либо звуковых библиотек, я не рассматриваю вообще, если кому очень интересно попробовать, ищите информацию в Интернете, он большой и там все есть*.
Часть 1. Создание класса спектрограммы
Листинг программы смотрите в приложении к журналу [12]. Создадим новый модуль для нашего класса, назовем его Spectrum. В нем описываем главный класс спектрограммы TAnalizer, и вспомагательные TChannel и TChannels для каналов частот. Класс TAnalizer мы наследуем от TPaintBox и добавляем к нему несколько нужных нам полей, это:
- Timer (нужен для анимации ниспадающих пиков, в принципе можно было бы обойтись и без собственного таймера, но так наш анализатор получает большую независимость от деталей реализации использующего его приложения)
- Buffer: TBitmap (буфер нужен чтобы не было мерцания при обновлении изображения)
- Channels (массив каналов, кол-во их будет настраиваемое)
Сами каналы у нас будут иметь градиентную заливку, которая задается полями LowColor: TColor; и HighColor: TColor.
Количество каналов и их координаты на экране задается в методе TChannels.SetCount. Для обратной связи с хозяином в классе TChannels предусмотрено поле Owner, которое заполняется сразу после его создания в конструкторе класса TSpectrum. Возможно, здесь я отошел от канонов. Обычно поле Owner в VCL используется немного по-другому, но в данном случае мне было удобнее сделать так и вообще я не претендую на академичность.
Подробно остановлюсь на методе Draw. В нем происходит обновление изображения в такой последовательности. Очищается буфер и заливается черным фоном, затем с помощью Win API функции GradientFill() мы заливаем градиентом все прямоугольники каналов. Функция GradientFill() довольно сложная и имеет много параметров, так что перед ее вызовом много строк кода занимает заполнение нужных структур для передачи в нее. Затем мы проверяем, есть ли пики, которые стали ниже уровня соответствующей им частоты с момента последней отрисовки, если таковые есть, обновляем их положение. Те пики, которые наоборот стали выше текущего уровня, мы пока не трогаем, они будут обработаны в таймере, чтобы они плавно падали вниз (но рисуются все они в этой процедуре, в таймере только смена позиций и вызов отрисовки). После этого мы закрасим черным (цветом фона) верхушки тех каналов, уровни которых ниже максимального. БылаТут я использованал функцияю из WinAPI – OffsetRect() для смещения прямоугольника канала вверх, иа затем, после закрашивания, повторный вызов OffsetRect(), только теперь вниз, для восстановления прежних координат прямоугольника канала. И в завершении рисуем пики (короткие горизонтальные линии) для всех каналов. После этого у нас имеется обновленное изображение спектрограммы в буфере, и мы выводим его на экран, а точнее на канву нашего объекта.
Чтобы изображение не пропадало с нашего компонента при перерисовке окна, мы обрабатываем унаследованный метод Paint и в ней вызываем нашу процедуру обновления Draw(). Хотя с тех пор, как я добавил таймер, это уже лишнее, можете убрать обработку Paint() если хотите, но если нет таймера, она необходима.
Класс написан, пора его протестировать. Для этого создадим простое приложение. Для чего, поместим на форму TGroupBox, чтобы удобнее было размещать наш анализатор, и две кнопки, по нажатию одной – будет задаваться случайный уровень для всех каналов (см. рисунок 1):
Рис. 1. Тестовая форма. Спектрограмма случайных величин
По нажатию другой – пройдет волна (см. рисунок 2):
Рис. 2. Тестовая форма. Спектрограмма волновой функции
Для волны нам понадобится таймер и подключение в Uses модуля Math, так как мы будем использовать синус. По нажатию третьей – все каналы по очереди заполнятся пиковым значением. Визуально это будет, похоже, как будто кто-то пальцем провел по клавишам пианино (см. рисунок 3):
Рис. 3. Тестовая форма. Спектрограмма в виде пилы
Смотрите листинг, компилируйте ,и разбирайтесь. Если все понятно, то переходим к части 2, игде будем подключать анализатор к плееру.
Часть 2. Подключение к BASS
Как это осуществить? Здесь я отдельно рассмотрю два случая, подключение к компоненту-оболочке TBassPlayer и непосредственно к BASS. Ну что-ж, приступим…
TBassPlayer
Хочу сразу обратить ваше внимание на нюансы с различными версиями как Delphi, так и TBassPlayer. Как вы знаете, начиная с D2009, для строк используется Unicode. Если у вас Delphi более ранних версий, можете дальше не читать и переходить к коду. Если же у вас Unicode версия, то придется внести некоторые изменения в исходники TBassPlayer, в его главный модуль, иначе работать не будет (у меня в D2009 возникали ошибки во время выполнения, связанные с некорректным форматом строк). Хотя в последней его версии и указана совместимость с D2009, да и по времени выхода (май 2009) уже по идее должна быть поддержка юникода, но мне пришлось лезть в исходники даже последней версии. Исправленный модуль вы найдете в файлах к статье [1].
Добавлю еще, что помимо функций оболочки над <bass.dll>, TBassPlayer добавляет поддержку Winamp — плагинов, визуализации и другие функции. Подробнее читайте в справке к компоненту.
Итак, вернемся к нашим баранам. Скопируйте в папку с проектом файл <Spectrum.pas> с нашим анализатором и подключите его в Uses. Я не стал устанавливать в среду TBassPlayer, не люблю я все подряд устанавливать, а просто включил его в Uses. Пути к компоненту можете указать в свойствах среды, а в директорию с программой нужно скопировать файл <bass.dll> подходящей версии (разные версии TBassPlayer работают с разной версией BASS, последняя версия компонента 2.1 может работать с последним BASS 2.4.5). В обработчике события OnCreate() главной формы создаем экземпляр TBassPlayer и назначаем ему на событие OnNewFFTData() процедуру DisplayFFTBand. Затем создаем объект анализатора и назначаем ему количество каналов соответствующее количеству каналов в TBassPlayer (константа NumFFTBands).
Код открытия файла и запуска я скопировал из «демки» к TBassPlayer, заодно там и отображение некоторых свойств файла я оставил, будем выводить их в метки TLabel. Своего я добавил только кнопку Play/Pause, для определения режима работы кнопки используется ее поле Tag (меняем с «0» на «1» попеременно), при нажатии кнопки читаем ее Tag и производим необходимые действия: меняем название, запускаем или останавливаем воспроизведение и меняем Tag.
Кроме этого, мы создаем процедуру ClearChannels. В ней происходит обнуление каналов, это будет происходить при паузе и остановке. Ну и самое интересное для нас – это метод DisplayFFTBand, который привязан к событию обновления данных спектра, в него приходит состояние каналов в виде объекта Bands: TBandOut. Мы просто перегоняем значения в наш Analizer.Channels и запускаем обновление Analizer.Draw. Вот и все. Как видите, подключение элементарное, инкапсуляция проявляет себя во всей красе, все детали скрыты в модулях с классами, и нужно сделать всего несколько телодвижений, для того чтобы приложение заработало.
BASS
Пример кода подключения к <bass.dll> и получения данных спектра любезно предоставил наш форумчанин Зарипов Равиль (ZuBy). Кстати он разрабатывает плеер на базе BASS, можете подробнее ознакомиться на его сайте www.zubymplayer.com.
Библиотеку BASS и API для разных языков вы можете скачать с официального сайта проекта www.un4seen.com.
В поставке с BASS идет оболочка для Delphi <bass.pas>. Скопируйте ее в папку с проектом и подключите ее в Uses. Тоже самое проделайте с <Spectrum.pas>. По сути, подключение анализатора мало отличается от случая с TBassPlayer. Также создаем объект, назначаем ему родителя, координаты и количество каналов.
Небольшие отличия есть в механизме получения данных о спектре. Если кто не знает, FFT – означает Fast Fourier Transorm, по-русски – Быстрое Преобразование Фурье или БПФ. Таким образом, FFT Data переводится как БПФ данные. В таймере мы получаем от BASS эти самые БПФ данные в массив FData и запускаем процедуру SpectrumRender. В ней проверяется, остановлено ли воспроизведение, если остановлено, то обнуляется массив FData. В связи с этим хочу заметить, что в этом случае наш таймер в классе TAnalizer является «пятым колесом», так как отрисовка спектра будет вызвана все равно, даже если воспроизведение остановлено. Он не мешает (нагрузку большую он не дает), но и не помогает, если хотите, можете встроить в класс команду по его отключению или вообще убрать таймер, тогда немного переделать функцию Draw нужно будет. Однако, в случае с TBassPlayer собственный таймер нам пригодился.
Но вернемся к процедуре – SpectrumRender. После проверки на остановку, мы заполняем значениями каналы анализатора и вызываем его отрисовку. В отличие от случая с TBassPlayer, где мы просто переносили значения без изменений, здесь мы имеем сырые БПФ данные, и расшифровку их мы должны делать самостоятельно. В данном случае мы берем, из массива с БПФ, данные, начиная с 5-го элемента, в количестве равном количеству каналов в спектрограмме. Правильно это или нет, я не знаю, и похоже мало кто вообще знает как правильно**, и в основном все возможно больше ориентируются на красивый вид спектрограммы, чем на точные значения частот. В любом случае, мы каналы не подписываем точным значением частоты, так что с нас взятки гладки.
Автор TBassPlayer применяет другой подход. Можете ознакомиться с ним в исходниках к компоненту [22]. А так как цель статьи – показать, как можно вывести на экран спектрограмму, а не разбор БПФ данных, то на этом скажем Фурье до-свидания и продолжим рассмотрение процедуры.
Итак, мы определились, какие элементы из массива будем брать. Затем мы умножаем значение на коэффициент, найденный пробным путем, он зависит от высоты спектрограммы. Судя по тому, что к полученному значению применена функция модуля Abs, значения могут иногда быть и отрицательными, оставляем это без изменений (напоминаю, что эта часть кода не моя, я только прикрутил сюда заполнение анализатора). Откидываем дробную часть с помощью функции Trunc(), и можем заполнять этими значениями каналы нашей спектрограммы. После заполнения вызываем отрисовку методом Analizer.Draw.
Таким образом, при каждом срабатывании таймера, анализатор будет заполняться новыми значениями и будет вызываться его отрисовка. В случае, если трек будет остановлен или поставлен на паузу, так же будет заполняться анализатор, только в этом случае нулями, и так же будет вызываться отрисовка. Это позволит нам увидеть, как пики плавно падают вниз. На этом закончим рассмотрение подключения к BASS.
Заключение
Итак, мы создали свой класс спектрограммы, который умеет вывести себя на экран, как по событию перерисовки, так и по таймеру, к тому же он еще и не мерцает при отрисовке и имеет удобный интерфейс. Также мы имеем образец повторно используемого кода, мы ведь его уже использовали как минимум три раза в разных проектах, первый раз в тесте, второй – при подключении к TBassPlayer, и третий раз при подключении напрямую к BASS.
Исходники тестовых модулей получения спектрограмм прилагаются в виде ресурсов в теме «Журнал клуба программистов. Третий выпуск» или непосредственно в архиве с журналом.
Ресурсы
- Типичные примеры обсуждения на форуме по получению спектрограммы через библиотеку BASS
http://www.programmersforum.ru/showthread.php?t=71069&highlight=%FD%EA%E2%E0%EB%E0%E9%E7%E5%F0
http://www.programmersforum.ru/showthread.php?t=89708&highlight=%FD%EA%E2%E0%EB%E0%E9%E7%E5%F0
http://www.programmersforum.ru/showthread.php?t=94224&highlight=%FD%EA%E2%E0%EB%E0%E9%E7%E5%F0
Модули и проекты, использованные в статье http://programmersclub.ru/pro/pro3.zip. Модули и проекты, использованные в статье http://programmersclub.ru/pro/pro3.zip
Статья из третьего выпуска журнала «ПРОграммист».
21st
Июн
Шифрование данных [обсуждение]
Нужно было добавить возможность шифрования файлов в своей программе (она путешествовала вместе с флешкой).
Программа приобрела более нужные и полезные функции и была переписана с Delphi на С# , однако вохможность шифрования я так и не реализовал.
В философии криптографии и “аутентичности” я ещё совсем зеленый)
Алгоритм для шифрования данных ,найденный мною, использовал 2 функции, шифровку и некий обратный аналог – дешифровку. Для программы я счел удобным шифруемые файлы удалять а их место занимали зашифрованные с добавленным расширением , которое по желанию пользователя можно было добавить в систему(в реестр) и файлы с таким расширением открывались бы моей программой.
Но вот в чем беда, если пытатся дешифровать файл неверным паролем – получим кучку непригодного мусора, и для восстановления его в первоначальный вид потребовалось бы очень много усилий.
Получается программа должна была сообщать юзеру что пароль неверный.Но как сохранить верный пароль? Можно к файлу добавлять зашифрованный пароль , которым пользователь шифровал основной файл, но есть свои НО. Ведь шифровать пароль нужно ещё 1ним паролем, а его прийдется вшить в программу , и соттветственно вытащить его оттуда не составит больших проблем.И вся работа коту под хвост 
В общем я остановился на 2х вариантах:
1) Шифровать пароль паролем , при попытке расшифровать файл, сначала открыть секцию с паролем, если пароль введенный юзером совпадает с результатом расшифровки тогда пароль верный и можно расшифровывать основной файл
2) В конец файла добавлять 2 секции. 1 аналогична 1вому варианту, 2рая – сгенерировать “стойкий” пароль для шифрации основного файла, и этот “стойкий” пароль зашифровать паролем юзера.
Дешифрация : Проверяем совпадают ли введенный пароль и дешифрация, дешифруем стойкий пароль, дешифруем файл.
Цвета обозначены.
Хотелось бы знать ваше мнение , услышать советы или идеи)
ПС. В дополнении к заголовку хотелось бы найти алгоритм , при котором шифруемый текст оставался читабельным и копируемым без всяких ??????? , наверно некое подобие замены по таблице.
Интересная тема на форуме. Принять участие.
20th
Июн
Нахождение точки на медиане треугольника в координатной плоскости.
Мои мысли: почти никаких
. Я вообще геометрические задачи в плоскости не решал. Помогите пожалуйста!Заранее спасибо.
Обычно пишу такие штуки вот так:
struct point
{
double x, y;
point (double xx, double yy) : x(xx), y(yy){}
point operator + (point &a)
{
return point(x+a.x, y+a.y);
}
point operator - (point &a)
{
return point(x-a.x, y-a.y);
}
point operator * (double v)
{
return point(x*v,y*v);
}
double length()
{
return sqrt(x*x+y*y);
}
point ort()
{
double l = length();
return point(x/l,y/l);
}
};
point getPointOnMedian(point A, point B, point C)
{
point M = (A+B)*0.5; // ибо M - середина AB
point BM = M-B; // BM - это вектор
point P = B + BM.ort()*10;
return P;
}
Надо лишь понять, что вектор и точка – это одно и то же. Ну и что такое орт.
20th
Задача о Ханойских башнях. Си.
Каждый студент-программист решает эту задачу. Вот код для упрощения поиска.
#include <stdio.h>
#include <conio.h>
void DisplayPin(int,int);
void TransferInNext( int,int,int,int);
void InputPin(int);
void Swap(int,int);
struct Pin
{
int pin[3][50];
int count[3];
}T;
int count=0;
int main()
{
int amount;
clrscr();
printf("Input amount rings->");
scanf("%d",&amount);
if(amount>50)
{
printf("<50\n");
return 1;
}
InputPin(amount);
if(amount)
TransferInNext( 1,3,2,amount);
printf("\nAmount step %d \n",count);
getch();
return 0;
}
void DisplayPin( int from,int to)
{
int i,j;
printf("\nMove Disk From %d To %d",from,to);
for(i = 0; i < 3; i++)
{
printf("\n");
printf("| ");
for(j = 0;j< T.count; j++)
printf("%d",T.pin[j]);
}
printf("\n");
}
void Swap(int from,int to)
{
T.pin[to-1][T.count[to-1]++]=T.pin[from-1][--T.count[from-1]];
DisplayPin(from,to);
count++;
}
//////////////////////////////////
void TransferInNext ( int from,int to,int via,int n)
{
if(n==1)
{
Swap(from,to);
}
else
{
TransferInNext(from,via,to,n-1);
Swap(from,to);
TransferInNext(via,to,from,n-1);
}
}
///////////////////////////////////
void InputPin(int len)
{
int i,k=len;
printf("| ");
for(i=0; i<len; i++,k--)
{
T.pin[0]=k;
printf("%d",T.pin[0]);
}
printf("\n| \n| \n---------------------------------------\n\n");
T.count[0]=len;
return;
}
Возможно, в теме на форуме будет доработан код для псевдографики.
19th
Июн
Правильное определение порядка точек в прорисовке тени в 2d-игре
Давно тут не постил уже, прям забыл уже как тут хорошо было

Вот заинтересовал вопрос о прорисовке тени. Допустим есть некий игрок и препятствия (см. вложение) и он имеет угол обзора 90 градусов и все, что выпадает за этот угол или закрывается препятствием не должно быть отображено. Допустим неким образом я сумел просчитать координаты этих точек, где кривая тени ломается. Определяю методом перебора:
1. Проверяю доступность для “обзора” каждой из четырех точек препятствия (все объекты – прямоугольники).
2. Если точка оказывается доступной(красные круги) то добавляем ее в список точек а также пробуем спроектировать луч из этой точки с углом из точки обзора, тем самим добавляя спроектированную точку в список(угол показан жёлтым, а спроектированная точка зелёным).
3. Дальше пытаюсь отсортировать эти точки так, чтобы их можно было бы прорисовать одним циклом (для начала просто соединить в ломанную(показано белым), а в дальнейшем хочу зарисовать все, не входящее в эту фигуру чёрным или серым). Но так как все препятствия расположены хаотически то может случится ситуация, когда одно препятствие попадает в список раньше положенного.
Прошу помощи в сортировке такого списке, чтобы можно было отобразить как на картинке
Спасибо за внимание.
Интересная проблема и возможные варианты решения. Смотрим на форуме. Принимаем участие.
18th
Июн
Целесообразность применения out
Для объявления выходных параметров служит ключевое слово out:
procedure GetScreenResolution(out Width, Height: Integer);
begin
Width := GetScreenWidth;
Height := GetScreenHeight;
end;
Установка значений выходных параметров внутри подпрограммы приводит к установке значений переменных, переданных в качестве аргументов:
var
W, H: Integer;
begin
GetScreenResolution(W, H);
...
end;
После вызова процедуры GetScreenResolution переменные W и H будут содержать значения, которые были присвоены формальным параметрам Width и Height соответственно.
Если параметр является одновременно и входным, и выходным, то он описывается с ключевым словом var:
procedure Exchange(var A, B: Integer);
var
C: Integer;
begin
C := A;
A := B;
B := C;
end;
Изменение значений var-параметров внутри подпрограммы приводит к изменению значений переменных, переданных в качестве аргументов:
var
X, Y: Integer;
begin
X := 5;
Y := 10;
...
Exchange(X, Y);
// Теперь X = 10, Y = 5
...
end;
При вызове подпрограмм на место out- и var-параметров можно подставлять только переменные, но не константы и не выражения.
Если при описании параметра не указано ни одно из ключевых слов const, out, или var, то параметр считается входным, его можно изменять, но все изменения не влияют на фактический аргумент, поскольку они выполняются с копией аргумента, создаваемой на время работы подпрограммы. При вызове подпрограммы на месте такого параметра можно использовать константы и выражения. Пример подпрограммы:
f
unction NumberOfSetBits(A: Cardinal): Byte;
begin
Result := 0;
while A <> 0 do
begin
Result := Result + (A mod 2);
A := A div 2;
end;
end;
Параметр A в приведенной функции является входным, но при этом он используется в качестве локальной переменной для хранения промежуточных данных.
PS ну а теперь сами подумайте где это можно применить
Тема на форуме.
17th
Июн
Рассылка. Выпуск 63.
От ведущего рассылки.
Добрый вечер читатели рассылки. Сегодня выходит 63ий выпуск рассылки. Сегодня у нас обзор интересных тем и дискуссий с форума.
17th
ICQ Client Delphi
16th
Июн
Как пользоваться пакетом java.io
try
{
mkdirs = new File(name).exists();
if (mkdirs)
{
File file = new File(name);
boolean success = file.delete();
success = file.createNewFile();
}
else
{
File file = new File(name);
boolean success = file.createNewFile();
}
}
catch (IOException ex)
{
Logger.getLogger(Switch.class.getName()).log(Level.SEVERE, null, ex);
}
16th
Сохранение web-страницы с картинками. Delphi.
procedure TForm1.Button1Click(Sender: TObject);
var ms:TmemoryStream;
begin
ms:=TmemoryStream.create;
idhttp1.Get('http://lio.lv/olimps/uzdevumi.php?show=1',ms);
ms.SaveToFile('1.html');
end;
Облако меток
ассемблер timer ShellExecute SaveToFile select советы программы массив TBitMap word MySQL ListView SQL pos random компоненты дата LoadFromFile form база данных сеть html php RichEdit indy строки Win Api tstringlist Image мысли макросы Edit ListBox office C/C++ memo графика StringGrid поиск canvas файл Pascal форма Файлы интернет excel Microsoft Office Excel winapi журнал ПРОграммист DelphiКупить рекламу на сайте за 1000 руб
пишите сюда - alarforum@yandex.ru
Да и по любым другим вопросам пишите на почту

пеллетные котлы

Пеллетный котел Emtas
Наши форумы по программированию:
- Форум Web программирование (веб)
- Delphi форумы
- Форумы C (Си)
- Форум .NET Frameworks (точка нет фреймворки)
- Форум Java (джава)
- Форум низкоуровневое программирование
- Форум VBA (вба)
- Форум OpenGL
- Форум DirectX
- Форум CAD проектирование
- Форум по операционным системам
- Форум Software (Софт)
- Форум Hardware (Компьютерное железо)


