AYFly: http://code.google.com/p/ayfly/sourc...yers/PT3Play.h
ZXTune: http://code.google.com/p/zxtune/sour...s/pt3_supp.cpp
Разумеется, там еще обвязка в обоих случаях имеется.
Вид для печати
AYFly: http://code.google.com/p/ayfly/sourc...yers/PT3Play.h
ZXTune: http://code.google.com/p/zxtune/sour...s/pt3_supp.cpp
Разумеется, там еще обвязка в обоих случаях имеется.
C луны свалился?
Сегодня существует сотни различных языков, большинство могут юзать DLL.
Это бред?
AY FLY та функция которую можно было бы юзать в трекере ГЛЮЧИТ (сжирает весь проц или что-то в этом роде). Мы с Ande'ром пытались ее починить, но он в скорости отошел от дел и больше не возвращался.
Что касается проигрывания - меня устраивает реализация AY Fly, там все варианты предусмотрены, и загрузка по имени файла и из памяти и тп.
---------- Post added at 18:30 ---------- Previous post was at 18:18 ----------
Вот тестовый код для AY Fly:
Насколько я сейчас понимаю IM2 это функция которую 50 раз в секунду вызывает AY Fly.Цитата:
Global song, wnid, a_vol.c = 15
#Window_0 = 1
ProcedureC.l IM2()
Static first, adr
If first=0:
adr = GetFunction(1, "ay_writeay")
first = 1
EndIf
a_freq = 612
CallCFunctionFast(adr, song, 0 , a_freq & $0000FF00, 0)
CallCFunctionFast(adr, song, 1 , a_freq & $000000FF, 0)
CallCFunctionFast(adr, song, 8 , a_vol, 0)
CallCFunctionFast(adr, song, 7 , %00111000, 0)
If a_vol: a_vol - 1: EndIf
EndProcedure
OpenWindow(#Window_0, 387, 71, 256, 192, "retracker", #PB_Window_ScreenCentered|#PB_Window_SystemMenu)
wnid = WindowID(#Window_0)
OpenLibrary(1, "ayfly.dll")
song = CallCFunction(1, "ay_initemptysong", 44100, @IM2())
CallCFunction(1, "ay_sethwnd", song, wnid)
CallCFunction(1, "ay_startsong" ,song)
Repeat
Event = WaitWindowEvent()
Select Event
Case #WM_LBUTTONDOWN
a_vol = 15
Case #PB_Event_CloseWindow: End
EndSelect
ForEver
---------- Post added at 18:35 ---------- Previous post was at 18:30 ----------
Для современного трекера это маловато, нужна возможность проигрывать Didital и подмешивать Beeper. Как это реализовать в реалиях Windows я хз (звуковые буферы и т.п.)
велосипедная куча библиотек вокруг каждой платформы. дотнетчики пишут своё, джависты - своё, питоновцы - своё и т.д.
я зачекаутил ayfly и zxtune и ужаснулся - сколько же всего понаписано, и я всё туда же, клепать велосипеды ))
так что будущее за избыточностью и велосипедами
согласен, для плеера, эмулятора и трекера нужны разные либы с разными АПИ. для трекера подойдёт что-то вроде BASS. то есть, манипуляция со звуком должна быть на уровне семплов, каналов и эффектов
создал семпл (цифровой или AY-like), поставил его играться в канал, и накладываешь эффекты прямо во время воспроизведения
если я правильно понял newart, ему нужно именно то, что есть в моей библиотеке :v2_yahoo:
то есть,
1) функция out_reg(t, num_reg, value), которая симулирует вывод в регистр на точно заданной микросекунде (все t должны неубывать. в идеале - строго возрастать)
2) возможность спросить у чипа: эй, а сколько семплов у тебя уже готово с учётом последнего t, на котором была запись?
3) возможность получить N семплов из буфера. но не больше, чем уже готово
---------- Post added at 21:14 ---------- Previous post was at 21:06 ----------
... а может быь и нет. может, newart-у нужен "живой" AY. чтобы DLL сама крутила цикл в отдельном потоке, сама постоянно выводила звук, а код трекера в реальном времени писал в регистры и не заморачивался с буферами и звукокартами :confused:
А каким боком умение написать DLL относится к кодингу на ZX?
...А может быть ворона, а может быть собака, а может быть корова! (что тоже хорошо :) )
У меня несколько частей в либе:
IO(read data) => Core{Process(search and convert raw data to track) => Tracking(interpret track data to device commands)} => Device(convert commands to sound dump) => Sound{Mixer(mix multichannel data to stereo)=>Backend(play sound)}
Эти части достаточно самостоятельны и отвязаны друг от друга. Путем адаптации любую из них можно юзать.
Зависит от задачи:
1. если делаем аналог Vortex, то достаточно AY эмулятора, который все делает сам, единсвенное что делаю я это устанавливаю регистры AY раз в 1/50с через callback функцию либы.
2. Если хочется возможность смешивать звук с Beeper (а ведь хочется), то нужна еще одна callback функция, в которой я накладывал бы семпл Beeper'a (генерируя его сам) на семпл AY сгеренрированый либой и она бы уже все это выводила в карту.
3. Beeper'a мало, хочется из 3-х AY каналов иметь хотя бы один цифровой. Тогда вариант со смешиванием семплов уже не подходит. Нужна видимо вот такая функция "функция out_reg(t, num_reg, value), которая симулирует вывод в регистр на точно заданной микросекунде (все t должны неубывать. в идеале - строго возрастать)" вместе с функционалом первого варианта. Если либа знает что такое Beeper, то возможно второй вариант по уму надо реализовывать через out_reg(beeper).
а вот это может обернуться проблемами. "сэмпл бипера", сделанный отдельно, без синхронизации всех генераторов звука, может не совпадать с длиной семпла AY. или погрешности окрулений кол-ва семплов в пересчёте на тики Z80/AY будет набегать и щёлкать периодически. можно не ставить себе цель микшировать всё в один буфер, а выводить несколько звуков одновременно, отдав микширование на откуп венде
именно так я и сделал
например, вывод в sound_drive:
update(t, left_vol, right_vol)
причём эмулятор AY - на выходе тот же sound_drive. он говорит звуковому движку, на каком такте AY поменялись регистры ЦАПов и всё
самое вкусное - если выводить с частотой 200KHz, либа сама красиво переведёт в 44.1/48/96 или сколько ей сказали при старте
это потому что задача неразрешимая в рамках имеющихся либ. с одной стороны, ты хочешь потактовую точность, а с другой стороны, не хочешь возиться со звуковыми буферами, а чтобы само играло
если играет само, асинхронно, то и точность плюс-минус лапоть. единственный выход - трекерное АПИ аля BASS. ты точно задаёшь параметры семпла (например, для бипера - амплитуду, скважность и т.п. для цифрового AY-семпла - сами данные семпла и частоту дискретизации), а потом, когда захочешь, ставишь семпл в канал. но такой подход потребует существенного дописывания библиотек
---------- Post added at 23:42 ---------- Previous post was at 23:35 ----------
хотя... не нужно делать метод "получить буффер", а нужно сделать метод "проиграть то, что накопилось в буфере". метод сделать синхронным: пока не доиграет - не возвращается. чтобы сами звуковые данные к тебе не приходили
если есть желание этим пользоваться, могу на выходных попробовать завернуть в DLL
как-то так:
beeper_tick и ay_tick - это 64-битные переменные. бейсик это поддерживает?Код:hMixer = create_mixer(44100, 100000 /* размер буфера в семплах */);
hBeeper = create_render(hMixer, 3500000 /* тактовая частота, в которой меряется beeper_tick */ );
beeper_tick = 0;
hAY = create_ay(hMixer, 3500000);
ay_tick = 0;
// зададим какой-нибудь тон и пусть играет. больше регистры трогать не будем
set_reg(hAY, 1, 10);
set_reg(hAY, 8, 15);
set_reg(hAY, 7, 254);
begin:
// на бипере будем играть прерывистые гудки
// пошёл гудок
x=0
for i=0 to 1000000 step 500
update(hBeeper, beeper_tick);
set_volume(hBeeper, x, x); // ставим x в левый и правый канал
x = 5000-x; // инвертируем бит на бипере
beeper_tick = beeper_tick + 500; // 500 тактов между фронтами = 3.5khz
next i
// пошла пауза
beeper_tick = beeper_tick + 1000000
update(hBeeper, beeper_tick);
play(hMixer); // здесь play ничего не проиграет, потому что к миксеру привязано два устройства, а вывод прошёл только в одно (play играет семплы до минимального из update-тиков всех устройств на миксере)
// теперь играем AY
// поскольку мы договорились, что он будет играть ровный тон,
// пусть всё делает сам - регистры не трогаем
ay_tick = ay_tick + 2000000; // важно: со времененм не должно сильно отставать или убегать от beeper_tick
update(hAY, ay_tick);
play(hMixer); // выводим, сколько накопилось. в принципе, можно вызывать вообще после любого update() - хуже не будет
goto begin; // зацикливаем
для использования в трекере тебе всё-таки придётся писать BASS-оподобный движок )) чтобы он сам крутился в цикле и делал нужные update() на девайсах, анализируя, какие семплы в каких каналах сейчас играют. всё это ещё должно быть интерактивным, играть от кнопок юзера. не пугает?
А что физически делают функции update и play?
update(hDevice, t) эмулирует устройство от предыдущего такта t0 до текушего t. у каждого девайса есть свой указатель записи в буфер миксера. все семплы, сгенерированные за период от t0 до t записываются (точнее, выполняется операция сложения) в буфер миксера.
play(hMixer) смотрит указатели записи всех девайсов на миксере, выбирает из них минимальный. у миксера есть указатель чтения из буфера. в буфере проссумированы и готовы к выводу семплы всех устройств от указателя чтения до минимального из указателей записи. этот фрагмент извлекается из буфера и ставится в очередь WAV-плеера. указатель чтения увеличивается на число семплов, переданных плееру.
можно для удобства разделить play на 2 функции:
get_ready_samples(hMixer) - чтобы узнать, сколько семплов в буфере готовы и play_samples(hMixer, n), чтобы передать из буфера миксера в плеер n семплов начиная от позиции чтения и увеличить позицию чтения на n
обнаружена и исправлена ошибка в определении длины трека, из-за которой некоторые треки обрезались где-то в середине
добавлена не-юникодная сборка Winamp-плагина для использования с AIMP2