//////////////////////////////// ////// 31EDO PIANO V. 0.1 ////// ///////// written in ///////// ////////// Pixilang ////////// ///////////// by ///////////// // Hs, also known as iShrimp, // //////// specially for //////// ///////// Warmplace.Ru ///////// //////////////////////////////// // Get screen size xsize = get_xsize(get_screen()) ysize = get_ysize(get_screen()) xsize2 = xsize div 2 ysize2 = ysize div 2 // N.of harmonics = 33. N.of samples = 64. spqa = new(33, 1, FLOAT32) // square of amplitude, real, imaginary parts spre = new(64, 1, FLOAT32) spim = new(64, 1, FLOAT32) re = new(64, 1, FLOAT32) // buffers for FFT function im = new(64, 1, FLOAT32) samp = new(64, 1, FLOAT32) // sample amplitudes from -1 to 1 audiosamp = new(64, 1, FLOAT32) // this is a copy for audio synthesizer wasedited = 0 // Generate initial wave and calculate its spectrum i = 0 while( i < 64) { amp = (sin(2*M_PI*i/64)+sin(2*M_PI*i/32))/2 samp[i] = amp*amp*amp i + 1 } directfft() copy(audiosamp, samp) // Calculate screen areas: spectrum, waveform, keyboard spcol = (xsize2 - 22) div 33 spwidth = spcol * 33 spx = xsize2 - spwidth - 1 wavewidth = xsize - spwidth - 44 separatorx = (wavewidth - spwidth) div 2 kw = xsize div 63 if kw < 8 { kw = 8 } octwidth = kw * 31 wholeoctaves = xsize div (octwidth) if wholeoctaves > 0 { startoct1 = 0 endoct1 = wholeoctaves + 1 offset1 = -(wholeoctaves * octwidth) div 2 - octwidth startkey1 = (offset1 + xsize2) div kw - 5 if startkey1 < 0 { startkey1 = 0 } endkey1 = (xsize2 - (wholeoctaves * octwidth) div 2) div kw + 5 if endkey1 < 0 { endkey1 = 0 } if endkey1 > 30 { endkey1 = 30 } startoct2 = 1 - endoct1 endoct2 = 1 offset2 = offset1 startkey2 = startkey1 endkey2 = endkey1 } else { startoct1 = 1 endoct1 = 1 offset1 = xsize2 - octwidth startkey1 = (offset1 + xsize2) div kw - 5 if startkey1 < 0 { startkey1 = 0 } endkey1 = 30 startoct2 = 1 endoct2 = 1 offset2 = -xsize2 startkey2 = 0 endkey2 = 30 - startkey1 } kvblock = ysize2 div 16 if kvblock <= 0 { kvblock = 1 } rowy1 = ysize2 - kvblock * 16 - 1 rowy2 = ysize2 - kvblock * 8 - 1 swh = rowy1 + ysize2 - 24 if swh < 32 { swh = 32 } nkeys = (endoct1 - startoct2 + 1) * 31 // Touch status: 0 - inactive, 1 - active // Keyboard status: 0 - inactive, 1 - being pressed, 2 - pressed, -1 - being released touch = new(16) touchx = new(16) touchy = new(16) clean (touch) clean (touchx) clean (touchy) key = new(nkeys) keyvoice = new(nkeys) // a voice corresponding to that key clean (key) clean (keyvoice) notefreq = new(nkeys, 1, FLOAT32) i = 0 while (i < nkeys) { notefreq[i] = 440 * pow(2, (i - 24) / 31 + startoct2) i + 1 } // Key colours kcwhite = #F0F0F0 kcgrey = #A0A0A0 kcblack = #303030 kcactive = #90F010 // Synthesizer samplerate = 44010 voices = 16 frequency = new(voices, 1, FLOAT32) phase = new(voices, 1, FLOAT32) // typically from 0 to 1. Result = cos(2*pi*phase). phasedelta = new(voices, 1, FLOAT32) // =frequency/samplerate amplitude = new(voices, 1, FLOAT32) // from 0 to 1 vmode = new(voices) // 0 - silent, 1 - pressed, 2 - released clean (vmode) queue = 0 // current voice to be activated decay1 = 1 - 0.3 / samplerate decay2 = 1 - 1.5 / samplerate // Audio visualization avsize = xsize avbuf = new(avsize) avready = 0 set_audio_callback(audio_callback, 0, samplerate, INT16, 1) text = "" fn audio_callback( $stream, $userdata, $channels, $frames, $output_time_in_system_ticks ) { $f = 0 while($f < $frames) { $output = 0.0 $v = 0 while($v < voices) { if vmode[$v] { $output + audiosamp[phase[$v] * 64] * amplitude[$v] if vmode[$v] == 1 { amplitude[$v] * decay1 } else { amplitude[$v] * decay2 } phase[$v] = mod(phase[$v] + phasedelta[$v], 1.0) } $v + 1 } $channels[0][$f] = $output * 4096 $f + 1 } if avready { if avsize > $frames { avsize = $frames } copy(avbuf, $channels[0], avsize) avready = 0 } ret(1) } while 1 { // Multitouch poka ne rabotaet na ustrojstvah Android while( get_event() ) { if EVT[ EVT_TYPE ] == EVT_QUIT { halt } if EVT[ EVT_TYPE ] == EVT_MOUSEBUTTONDOWN { touch[0] = 1 touchx[0] = EVT[ EVT_X ] touchy[0] = EVT[ EVT_Y ] } if EVT[ EVT_TYPE ] == EVT_MOUSEMOVE && touch[0] == 1 { touchx[0] = EVT[ EVT_X ] touchy[0] = EVT[ EVT_Y ] } if EVT[ EVT_TYPE ] == EVT_MOUSEBUTTONUP { touch[0] = 0 } if EVT[ EVT_TYPE ] == EVT_TOUCHBEGIN { touch[ EVT[ EVT_SCANCODE ] ] = 1 touchx[ EVT[ EVT_SCANCODE ] ] = EVT[ EVT_X ] touchx[ EVT[ EVT_SCANCODE ] ] = EVT[ EVT_Y ] } if EVT[ EVT_TYPE ] == EVT_TOUCHMOVE && touch[ EVT[ EVT_SCANCODE ] ] == 1 { touchx[ EVT[ EVT_SCANCODE ] ] = EVT[ EVT_X ] touchx[ EVT[ EVT_SCANCODE ] ] = EVT[ EVT_Y ] } if EVT[ EVT_TYPE ] == EVT_TOUCHEND { touch[ EVT[ EVT_SCANCODE ] ] = 0 } } // Touch processing i = 0 while (i < nkeys) { if key[i] < 0 { key[i] = 0 } if key[i] > 0 { key[i] = -1 } i + 1 } editwave = 0 editspec = 0 i = 0 while (i < 16) { if touch[i] { if touchy[i]>=-ysize2 && touchy[i]<=swh-ysize2 // Upper part editing { n = (touchx[i]+xsize2)*64 div wavewidth // Waveform if n>=0 && n<64 { samp[n] = 1-2*(touchy[i]+ysize2)/swh editwave = 1 } n = (touchx[i]-spx) div spcol // Spectrum if n>=0 && n<33 && editwave == 0 { amp = pow(10, 0.4 - 8.8*(touchy[i]+ysize2)/swh) * 4096 // square of amplitude if spqa[n]>0.0000000001 { amp = sqrt (amp / spqa[n]) // Get a coefficient spre[n] * amp spim[n] * amp if n>0 && n<32 // Not to multiply 0th and 32nd harmonics twice { spre[64-n] * amp // Po rezul'tatu FFT garmoniki 0 i 32 - dejstvitel'nye chisla spim[64-n] * amp // (tol'ko kosinus); garmoniki 1...31 simmetrichny 63...33. } spqa[n] * amp * amp } else // Vo izbezhanie delenija na 0, ili na pochti 0 { spre[n] = amp spim[n] = 0 if n>0 // There is no 64th element { spre[64-n] = amp spim[64-n] = 0 } spqa[n] = amp * amp } editspec = 1 } } else { if touchy[i] >= rowy2 // Lower row { x0 = touchx[i] - offset2 y0 = touchy[i] - rowy2 o = x0 div octwidth // N.of octave, 0 is first from the left in row x0 - o * octwidth n = findkey(x0 div kw, y0 div kvblock) + o * 31 key[n] = 1 - key[n] // 0 -> 1, -1 -> 2 } else { if touchy[i] >= rowy1 // Upper row { x0 = touchx[i] - offset1 y0 = touchy[i] - rowy1 o = x0 div octwidth // N.of octave, 0 is first from the left in row x0 - o * octwidth n = findkey(x0 div kw, y0 div kvblock) + (o + startoct1 - startoct2) * 31 key[n] = 1 - key[n] // 0 -> 1, -1 -> 2 } } } } i + 1 } if editwave { directfft() } else // Editing waveform has greater priority { if editspec { inversefft() } else { if wasedited == 1 // if nothing is being edited now, but something was 1 frame ago: { spqa[0] = 0 // remove the constant offset spre[0] = 0 spim[0] = 0 inversefft() // strogo govorja, mozhno bylo i proshche amp = op_cn(OP_MAXABS, samp, 0) // find the maximum and normalize if amp > 1 { op_cn(OP_DIV, samp, amp) op_cn(OP_DIV, spre, amp) op_cn(OP_DIV, spim, amp) op_cn(OP_DIV, spqa, amp * amp) } copy(audiosamp, samp) } } } if editwave | editspec { wasedited = 1 } else { wasedited = 0 } clean(text) i = 0 while (i < nkeys) { //if key[i] > 0 { num_to_str(text, i) } if key[i] == 1 { vmode[queue] = 0 frequency[queue] = notefreq[i] phase[queue] = 0 phasedelta[queue] = frequency[queue] / samplerate amplitude[queue] = 1 keyvoice[i] = queue vmode[queue] = 1 queue + 1 if queue >= voices { queue = 0 } } if key[i] == -1 { vmode[keyvoice[i]] = 2 } i + 1 } transp(96) // Clear screen and display the data clear() fbox (-xsize2, -ysize2, wavewidth, swh, #106000) fbox (spx, -ysize2, spwidth, swh, #503000) transp(255) x0 = -wavewidth / 128.0 - xsize2 y0 = (1 - samp[63]) * swh / 2 - ysize2 i = 0 while (i < 64) // Draw the waveform { x1 = (wavewidth * (i * 2 + 1)) / 128.0 - xsize2 y1 = (1 - samp[i]) * swh / 2 - ysize2 line (x0, y0, x1, y1, #20F000) fbox (x1-1, y1-1, 3, 3, #20F000) x0 = x1 y0 = y1 i + 1 } line (x0, y0, wavewidth - xsize2, (y1 + (1 - samp[0]) * swh / 2 - ysize2) / 2, #20F000) transp(160) i = 0 while (i < 33) // Draw the spectrum { amp = spqa[i] / 4096 // this is square of amplitude; from 0 to 1 if amp > 0.0000000001 {amp = log10(amp)} else {amp = -10.0} // 1 unit = 5 dBs; form 0 to -50 dB y0 = (0.4 - amp) * swh / 8.8 - ysize2 // y axis from 2 to -42 dB y1 = swh - ysize2 if y0 >= y1 {y0 = y1 - 1} fbox(spcol * i + spx, y0, spcol - 1, y1 - y0, #F08000) i + 1 } transp(255) // Draw the keyboard o = startoct1 while (o <= endoct1) // Draw first keyboard row { if o == startoct1 {k0 = startkey1} else {k0 = 0} if o == endoct1 {k1 = endkey1} else {k1 = 30} octx = offset1 + (o - startoct1) * octwidth drawkbd(rowy1) o + 1 } o = startoct2 while (o <= endoct2) // Draw second keyboard row { if o == startoct2 {k0 = startkey2} else {k0 = 0} if o == endoct2 {k1 = endkey2} else {k1 = 30} octx = offset2 + (o - startoct2) * octwidth drawkbd(rowy2) o + 1 } print ("WAVEFORM", -xsize2, -ysize2, #90D060, TOP | LEFT)// Draw text labels print ("HARMONICS", xsize2, -ysize2, #D0A060, TOP | RIGHT) print ("0dB", spx, 2 * swh div 44 - ysize2, #C0C0C0, RIGHT) print ("-10dB", spx, 12 * swh div 44 - ysize2, #C0C0C0, RIGHT) print ("-20dB", spx, 22 * swh div 44 - ysize2, #C0C0C0, RIGHT) print ("-30dB", spx, 32 * swh div 44 - ysize2, #C0C0C0, RIGHT) print ("-40dB", spx, 42 * swh div 44 - ysize2, #C0C0C0, RIGHT) print ("0", spcol div 2 + spx, swh - ysize2 + 1, #C0C0C0, TOP) print ("2", 5 * spcol div 2 + spx, swh - ysize2 + 1, #C0C0C0, TOP) print ("4", 9 * spcol div 2 + spx, swh - ysize2 + 1, #C0C0C0, TOP) print ("8", 17 * spcol div 2 + spx, swh - ysize2 + 1, #C0C0C0, TOP) print ("16", 33 * spcol div 2 + spx, swh - ysize2 + 1, #C0C0C0, TOP) print ("24", 49 * spcol div 2 + spx, swh - ysize2 + 1, #C0C0C0, TOP) print ("32^", xsize2, swh - ysize2 + 1, #C0C0C0, TOP | RIGHT) print ("31EDO keyboard", 0, rowy1 - 1, #F080F0, BOTTOM) print (text, 0, -10) frame() } fn drawkbd($yoffset) // neoptimal'no, no XPEH 3HAET kak lutshe napisat' { if 1 >= k0 && 1 <= k1 {fbox(octx, $yoffset, kw * 4 - 1, kvblock * 8 - 1, kcwhite)} if 6 >= k0 && 6 <= k1 {fbox(octx + kw * 4, $yoffset, kw * 5 - 1, kvblock * 8 - 1, kcwhite)} if 11 >= k0 && 11 <= k1 {fbox(octx + kw * 9, $yoffset, kw * 4 - 1, kvblock * 8 - 1, kcwhite)} if 14 >= k0 && 14 <= k1 {fbox(octx + kw * 13, $yoffset, kw * 4 - 1, kvblock * 8 - 1, kcwhite)} if 19 >= k0 && 19 <= k1 {fbox(octx + kw * 17, $yoffset, kw * 5 - 1, kvblock * 8 - 1, kcwhite)} if 24 >= k0 && 24 <= k1 {fbox(octx + kw * 22, $yoffset, kw * 5 - 1, kvblock * 8 - 1, kcwhite)} if 29 >= k0 && 29 <= k1 {fbox(octx + kw * 27, $yoffset, kw * 4 - 1, kvblock * 8 - 1, kcwhite)} if 0 >= k0 && 0 <= k1 {fbox(octx, $yoffset, kw - 1, kvblock * 6 - 1, kcgrey)} if 2 >= k0 && 2 <= k1 {fbox(octx + kw * 2, $yoffset, kw * 2 - 1, kvblock * 6 - 1, kcgrey)} if 5 >= k0 && 5 <= k1 {fbox(octx + kw * 4, $yoffset, kw * 2 - 1, kvblock * 6 - 1, kcgrey)} if 7 >= k0 && 7 <= k1 {fbox(octx + kw * 7, $yoffset, kw * 2 - 1, kvblock * 6 - 1, kcgrey)} if 10 >= k0 && 10 <= k1 {fbox(octx + kw * 9, $yoffset, kw * 2 - 1, kvblock * 6 - 1, kcgrey)} if 12 >= k0 && 12 <= k1 {fbox(octx + kw * 12, $yoffset, kw - 1, kvblock * 6 - 1, kcgrey)} if 13 >= k0 && 13 <= k1 {fbox(octx + kw * 13, $yoffset, kw - 1, kvblock * 6 - 1, kcgrey)} if 15 >= k0 && 15 <= k1 {fbox(octx + kw * 15, $yoffset, kw * 2 - 1, kvblock * 6 - 1, kcgrey)} if 18 >= k0 && 18 <= k1 {fbox(octx + kw * 17, $yoffset, kw * 2 - 1, kvblock * 6 - 1, kcgrey)} if 20 >= k0 && 20 <= k1 {fbox(octx + kw * 20, $yoffset, kw * 2 - 1, kvblock * 6 - 1, kcgrey)} if 23 >= k0 && 23 <= k1 {fbox(octx + kw * 22, $yoffset, kw * 2 - 1, kvblock * 6 - 1, kcgrey)} if 25 >= k0 && 25 <= k1 {fbox(octx + kw * 25, $yoffset, kw * 2 - 1, kvblock * 6 - 1, kcgrey)} if 28 >= k0 && 28 <= k1 {fbox(octx + kw * 27, $yoffset, kw * 2 - 1, kvblock * 6 - 1, kcgrey)} if 30 >= k0 && 30 <= k1 {fbox(octx + kw * 30, $yoffset, kw - 1, kvblock * 6 - 1, kcgrey)} if 3 >= k0 && 3 <= k1 {fbox(octx + kw * 3, $yoffset, kw - 1, kvblock * 4 - 1, kcblack)} if 4 >= k0 && 4 <= k1 {fbox(octx + kw * 4, $yoffset, kw - 1, kvblock * 4 - 1, kcblack)} if 8 >= k0 && 8 <= k1 {fbox(octx + kw * 8, $yoffset, kw - 1, kvblock * 4 - 1, kcblack)} if 9 >= k0 && 9 <= k1 {fbox(octx + kw * 9, $yoffset, kw - 1, kvblock * 4 - 1, kcblack)} if 16 >= k0 && 16 <= k1 {fbox(octx + kw * 16, $yoffset, kw - 1, kvblock * 4 - 1, kcblack)} if 17 >= k0 && 17 <= k1 {fbox(octx + kw * 17, $yoffset, kw - 1, kvblock * 4 - 1, kcblack)} if 21 >= k0 && 21 <= k1 {fbox(octx + kw * 21, $yoffset, kw - 1, kvblock * 4 - 1, kcblack)} if 22 >= k0 && 22 <= k1 {fbox(octx + kw * 22, $yoffset, kw - 1, kvblock * 4 - 1, kcblack)} if 26 >= k0 && 26 <= k1 {fbox(octx + kw * 26, $yoffset, kw - 1, kvblock * 4 - 1, kcblack)} if 27 >= k0 && 27 <= k1 {fbox(octx + kw * 27, $yoffset, kw - 1, kvblock * 4 - 1, kcblack)} } fn findkey($kx, $ky) // Plohoj kod? Byvaet i huzhe... { if $kx <= 3 {$kn = 1} else { if $kx <= 8 {$kn = 6} else { if $kx <= 12 {$kn = 11} else { if $kx <= 16 {$kn = 14} else { if $kx <= 21 {$kn = 19} else { if $kx <= 26 {$kn = 24} else {$kn = 29} } } } } } if $ky < 6 { if $kx == 0 {$kn = 0} else { if $kx == 2 || $kx == 3 {$kn = 2} else { if $kx == 4 || $kx == 5 {$kn = 5} else { if $kx == 7 || $kx == 8 {$kn = 7} else { if $kx == 9 || $kx == 10 {$kn = 10} else { if $kx == 12 {$kn = 12} else { if $kx == 13 {$kn = 13} else { if $kx == 15 || $kx == 16 {$kn = 15} else { if $kx == 17 || $kx == 18 {$kn = 18} else { if $kx == 20 || $kx == 21 {$kn = 20} else { if $kx == 22 || $kx == 23 {$kn = 23} else { if $kx == 25 || $kx == 26 {$kn = 25} else { if $kx == 27 || $kx == 28 {$kn = 28} else { if $kx == 30 {$kn = 30} } } } } } } } } } } } } } if $ky < 4 { if $kx==3||$kx==4||$kx==8||$kx==9||$kx==16||$kx==17||$kx==21||$kx==22||$kx==26||$kx==27 {$kn = $kx} } } ret ($kn) } fn directfft() { copy (re, samp) clean (im) fft (0, im, re, 64) copy (spre, re) copy (spim, im) op_cc (OP_MUL, re, re, 0, 0, 33) op_cc (OP_MUL, im, im, 0, 0, 33) op_cc (OP_ADD, re, im, 0, 0, 33) copy (spqa, re, 0, 0, 33) // Amplituda hranitsja v vide kvadrata amplitudy } fn inversefft() { copy (re, spre) copy (im, spim) fft (1, im, re, 64) copy (samp, re) }