| 47 | | wave_info_ex* CWaveEditorCtrl::getWave() { |
| 48 | | if (currentWave<0 || currentWave>=view->mainFrame->player->getWaves()) return 0; |
| 49 | | |
| 50 | | return view->mainFrame->player->getWave(currentWave); |
| 51 | | } |
| 52 | | |
| 53 | | int CWaveEditorCtrl::getWaveLevel() { |
| 54 | | wave_info_ex* entry=getWave(); |
| 55 | | if (entry==0) return -1; |
| 56 | | int numlevels=entry->get_levels(); |
| 57 | | if (currentLevel<0 || currentLevel>=numlevels) return -1; |
| 58 | | return currentLevel; |
| 59 | | } |
| 60 | | |
| 61 | | |
| 62 | | // assume we're working within 0x8000 |
| 63 | | int getSampleAt(void* buffer, int waveChannels, int waveFormat, float sample, int interleave) { |
| 64 | | DWORD temp; |
| 65 | | float tempF; |
| 66 | | float* fbuffer; |
| 67 | | char* cbuffer;//=(char*)buffer; |
| 68 | | switch (waveFormat) { |
| 69 | | case wave_buffer_type_si16: |
| 70 | | return ((short*)buffer)[(size_t)sample*waveChannels + interleave*2]; |
| 71 | | case wave_buffer_type_si24: |
| 72 | | cbuffer=(char*)buffer; // aligned by 1 byte?? |
| 73 | | temp=*(DWORD*)(&cbuffer[(size_t)sample*waveChannels*3 + interleave*3]) & 0x00ffffff; |
| 74 | | if (temp & 0x00800000) temp = temp | 0xFF000000; |
| 75 | | return (signed short)((int)temp >> 8) ;//cbuffer[sample*waveChannels*3]; |
| 76 | | case wave_buffer_type_si32: |
| 77 | | cbuffer=(char*)buffer; |
| 78 | | temp=*(DWORD*)(&cbuffer[(size_t)sample*waveChannels*4 + interleave*4]); |
| 79 | | return (signed short)(temp >> 16) ;//cbuffer[sample*waveChannels*3]; |
| 80 | | case wave_buffer_type_f32: |
| 81 | | cbuffer=(char*)buffer; |
| 82 | | fbuffer=(float*)(&cbuffer[(size_t)sample*waveChannels*4 + interleave*4]); |
| 83 | | tempF=*fbuffer; |
| 84 | | tempF*=0x8000; |
| 85 | | return tempF; |
| 86 | | } |
| 87 | | return 0; |
| 88 | | } |
| 96 | | // sizes and positioning are determined by the constants defined on top of this page |
| 97 | | int width=rc.right-rc.left; |
| 98 | | int height=(rc.bottom-rc.top) - 20; // reserve 20px for timeline |
| 99 | | |
| 100 | | view->mainFrame->player->lock(); |
| 101 | | wave_info_ex* wave=getWave(); |
| 102 | | if (wave==0) { |
| 103 | | view->mainFrame->player->unlock(); |
| 104 | | return DefWindowProc(); |
| 105 | | } |
| 106 | | |
| 107 | | int waveChannels=wave->get_stereo()?2:1; |
| 108 | | |
| 109 | | int displaySamples=(endDisplaySample-beginDisplaySample)+1; |
| 110 | | |
| 111 | | paintDelta=(double)(displaySamples) / (double)width; |
| 112 | | |
| 113 | | double sample=beginDisplaySample; |
| | 57 | // paint sample channels |
| | 58 | int width = rc.right-rc.left; |
| | 59 | int height = (rc.bottom-rc.top) - 20; // reserve 20px for timeline |
| | 60 | |
| | 61 | int fullHeight = height; |
| | 62 | |
| | 63 | if (waveInfo.stereo) { |
| | 64 | height = (height - 4) / 2; |
| | 65 | RECT rcL = { 0, 0, width, height }; |
| | 66 | RECT rcR = { 0, height + 4, width, height * 2 + 4 }; |
| | 67 | paintSampleChannel(dc, rcL, 0); |
| | 68 | paintSampleChannel(dc, rcR, 1); |
| | 69 | // paint splitter |
| | 70 | RECT rcSplit = { 0, height, width, height + 4}; |
| | 71 | dc.FillSolidRect(&rcSplit, GetSysColor(COLOR_3DFACE)); |
| | 72 | } else { |
| | 73 | RECT rcL = { 0, 0, width, height }; |
| | 74 | paintSampleChannel(dc, rcL, 0); |
| | 75 | } |
| | 76 | |
| | 77 | |
| | 78 | // paint timeline |
| | 79 | CRect tlrc(0, fullHeight+1, width, fullHeight+20); |
| | 80 | paintTimeline(dc, tlrc); |
| | 81 | |
| | 82 | // if looping points are set, draw them with protracker-type handles |
| | 83 | int beginLoopX = (float)(waveInfo.beginLoop - beginDisplaySample) / paintDelta; |
| | 84 | int endLoopX = (float)(waveInfo.endLoop - beginDisplaySample) / paintDelta - 1; |
| | 85 | |
| | 86 | CPen loopPen; |
| | 87 | loopPen.CreatePen(PS_SOLID, 1, 0x00FF00); |
| | 88 | |
| | 89 | dc.SelectPen(loopPen); |
| | 90 | dc.MoveTo(beginLoopX, fullHeight); |
| | 91 | dc.LineTo(beginLoopX, 0); |
| | 92 | dc.LineTo(beginLoopX+8, 0); |
| | 93 | dc.LineTo(beginLoopX+8, 8); |
| | 94 | dc.LineTo(beginLoopX, 8); |
| | 95 | |
| | 96 | dc.MoveTo(endLoopX, fullHeight); |
| | 97 | dc.LineTo(endLoopX, 0); |
| | 98 | dc.LineTo(endLoopX-8, 0); |
| | 99 | dc.LineTo(endLoopX-8, 8); |
| | 100 | dc.LineTo(endLoopX, 8); |
| | 101 | |
| | 102 | return DefWindowProc(); |
| | 103 | } |
| | 104 | |
| | 105 | inline float sample_scale(wave_buffer_type format, void* buffer) { |
| | 106 | unsigned int i; |
| | 107 | switch (format) { |
| | 108 | case wave_buffer_type_si16: |
| | 109 | return static_cast<float>(*(short*)buffer) / 0x7fff; |
| | 110 | case wave_buffer_type_si24: |
| | 111 | i = (*(unsigned int*)buffer) & 0x00ffffff; |
| | 112 | if (i & 0x00800000) i = i | 0xFF000000; |
| | 113 | return static_cast<float>((int)i) / 0x007fffff; |
| | 114 | case wave_buffer_type_si32: |
| | 115 | return static_cast<float>(*(int*)buffer) / 0x7fffffff; |
| | 116 | case wave_buffer_type_f32: |
| | 117 | return *(float*)buffer; |
| | 118 | default: |
| | 119 | return 0; |
| | 120 | } |
| | 121 | } |
| | 122 | |
| | 123 | int get_format_bytes(wave_buffer_type format) { |
| | 124 | switch (format) { |
| | 125 | case wave_buffer_type_si16: |
| | 126 | return 2; |
| | 127 | case wave_buffer_type_si24: |
| | 128 | return 3; |
| | 129 | case wave_buffer_type_f32: |
| | 130 | case wave_buffer_type_si32: |
| | 131 | return 4; |
| | 132 | default: |
| | 133 | return 2; |
| | 134 | } |
| | 135 | } |
| | 136 | |
| | 137 | void scan_min_max(wave_buffer_type format, void* buffer, int num_samples, bool stereo, int channel, float& out_min, float& out_max) { |
| | 138 | int sample_size = get_format_bytes(format); |
| | 139 | out_min = 0;//std::numeric_limits<float>::max(); |
| | 140 | out_max = 0;//std::numeric_limits<float>::min(); |
| | 141 | |
| | 142 | char* pbuf = (char*)buffer; |
| | 143 | for (int i = 0; i < num_samples; i++) { |
| | 144 | if (channel == 0) { |
| | 145 | float s = sample_scale(format, pbuf); |
| | 146 | if (s < out_min) |
| | 147 | out_min = s; |
| | 148 | if (s > out_max) |
| | 149 | out_max = s; |
| | 150 | |
| | 151 | } |
| | 152 | pbuf += sample_size; |
| | 153 | if (stereo) { |
| | 154 | if (channel == 1) { |
| | 155 | float s = sample_scale(format, pbuf); |
| | 156 | if (s < out_min) |
| | 157 | out_min = s; |
| | 158 | if (s > out_max) |
| | 159 | out_max = s; |
| | 160 | } |
| | 161 | pbuf += sample_size; |
| | 162 | } |
| | 163 | |
| | 164 | } |
| | 165 | } |
| | 166 | |
| | 167 | void CWaveEditorCtrl::paintSampleChannel(CDC& dc, RECT rc, int channel) { |
| | 168 | |
| | 169 | int width = rc.right - rc.left; |
| | 170 | int height = rc.bottom - rc.top; |
| | 171 | int waveChannels = waveInfo.stereo?2:1; |
| | 172 | |
| 116 | | long minSelect=std::min(beginSelectSample, endSelectSample); |
| 117 | | long maxSelect=std::max(beginSelectSample, endSelectSample); |
| 118 | | |
| 119 | | void* sampleBuffer=wave->get_sample_ptr(currentLevel); |
| 120 | | |
| 121 | | int sampleSamples=wave->get_sample_count(currentLevel); |
| 122 | | int waveFormat=wave->get_wave_format(currentLevel); |
| 123 | | |
| 124 | | int fullHeight=height; |
| 125 | | if (waveChannels==2) { |
| 126 | | height/=2; |
| 127 | | height-=2; |
| 128 | | } |
| 129 | | |
| 130 | | int x=0, y=height/2, y2=height/2; |
| 131 | | |
| 132 | | for (int i=0; i<width; i++) { |
| 133 | | if (!sampleBuffer) break; |
| 134 | | int sampleOfs=floor(sample); |
| 135 | | if (sampleOfs>endDisplaySample || sampleOfs>=sampleSamples) break; |
| 136 | | |
| 137 | | float v=(float)getSampleAt(sampleBuffer, waveChannels, waveFormat, sampleOfs, 0); |
| 138 | | v = (v/(float)0x8000)*(float)(height/2); |
| 139 | | |
| 140 | | if (sampleOfs>=minSelect && sampleOfs<maxSelect+1) |
| 141 | | dc.SelectStockPen(WHITE_PEN); else |
| 142 | | dc.SelectStockPen(BLACK_PEN); |
| 143 | | |
| 144 | | dc.MoveTo(i, 0); |
| 145 | | dc.LineTo(i, 0+height); |
| 146 | | |
| 147 | | if (sampleOfs>=minSelect && sampleOfs<maxSelect+1) |
| | 175 | |
| | 176 | CPenHandle oldpen = dc.SelectPen(selectedPen); |
| | 177 | |
| | 178 | int sampleSize = get_format_bytes(waveInfo.type); |
| | 179 | |
| | 180 | double sample = beginDisplaySample; |
| | 181 | |
| | 182 | dc.FillSolidRect(&rc, 0); |
| | 183 | |
| | 184 | double selectDelta = paintDelta>1?paintDelta:0; |
| | 185 | |
| | 186 | for (int i = 0; i < width; i++) { |
| | 187 | if (!waveInfo.bits) break; |
| | 188 | int sampleOfs = floor(sample); |
| | 189 | if (sampleOfs > beginDisplaySample + numDisplaySamples || sampleOfs >= waveInfo.samples) break; |
| | 190 | |
| | 191 | char* pbuf = (char*)waveInfo.bits + sampleOfs * sampleSize * waveChannels; |
| | 192 | |
| | 193 | float out_min, out_max; |
| | 194 | scan_min_max((wave_buffer_type)waveInfo.type, pbuf, paintDelta>1?(int)paintDelta:1, waveInfo.stereo, channel, out_min, out_max); |
| | 195 | |
| | 196 | out_min *= (float)height/2; |
| | 197 | out_max *= (float)height/2; |
| | 198 | |
| | 199 | if (sampleOfs >= beginSelectSample-selectDelta && sampleOfs <= endSelectSample-selectDelta) { |
| | 200 | dc.SelectStockPen(WHITE_PEN); |
| | 201 | dc.MoveTo(i, rc.top); |
| | 202 | dc.LineTo(i, rc.top+height); |
| | 203 | } |
| | 204 | |
| | 205 | if (sampleOfs >= beginSelectSample-selectDelta && sampleOfs <= endSelectSample-selectDelta) |
| 151 | | dc.MoveTo(x, 0+y); |
| 152 | | dc.LineTo(i, 0+v + height/2); |
| 153 | | |
| 154 | | if (waveChannels==2) { |
| 155 | | // tegne nederste? for svarte - må jo kunne gjøre dette litt smoothere |
| 156 | | float v2=(float)getSampleAt(sampleBuffer, waveChannels, waveFormat, sampleOfs, 1); |
| 157 | | v2 = (v2/(float)0x8000)*(float)(height/2); |
| 158 | | |
| 159 | | if (sampleOfs>=minSelect && sampleOfs<maxSelect+1) |
| 160 | | dc.SelectStockPen(WHITE_PEN); else |
| 161 | | dc.SelectStockPen(BLACK_PEN); |
| 162 | | |
| 163 | | dc.MoveTo(i, height+4); |
| 164 | | dc.LineTo(i, height+4+height); |
| 165 | | |
| 166 | | if (sampleOfs>=minSelect && sampleOfs<maxSelect+1) |
| 167 | | dc.SelectPen(selectedPen); else |
| 168 | | dc.SelectStockPen(WHITE_PEN); |
| 169 | | |
| 170 | | dc.MoveTo(x, height+4+y2); |
| 171 | | dc.LineTo(i, height+4+v2 + height/2); |
| 172 | | |
| 173 | | y2=v2+height/2; |
| 174 | | } |
| 175 | | |
| 176 | | x=i; |
| 177 | | y=v+height/2; |
| 178 | | |
| 179 | | sample+=paintDelta; // kanskje vi skal gange med channels her? mulig at vi bare får se halve stereosampels |
| 180 | | } |
| 181 | | |
| 182 | | view->mainFrame->player->unlock(); |
| 183 | | |
| 184 | | |
| 185 | | CRect tlrc(0, fullHeight+1, width, fullHeight+20); |
| 186 | | dc.FillRect(&tlrc, COLOR_BTNFACE); |
| 187 | | dc.Draw3dRect(&tlrc, GetSysColor(COLOR_BTNHIGHLIGHT), GetSysColor(COLOR_BTNSHADOW));//0, height, width, height+20); |
| | 209 | dc.MoveTo(i, rc.top + out_min + height/2); |
| | 210 | dc.LineTo(i, rc.top + out_max + height/2 + 1); |
| | 211 | |
| | 212 | sample += paintDelta; |
| | 213 | } |
| | 214 | |
| | 215 | dc.SelectPen(oldpen); |
| | 216 | |
| | 217 | } |
| | 218 | |
| | 219 | void CWaveEditorCtrl::paintTimeline(CDC& dc, RECT rc) { |
| | 220 | |
| | 221 | int width = rc.right - rc.left; |
| | 222 | int fullHeight = rc.top; |
| | 223 | |
| | 224 | dc.FillRect(&rc, COLOR_BTNFACE); |
| | 225 | |
| | 226 | CPen lightPen; |
| | 227 | lightPen.CreatePen(PS_SOLID, 1, GetSysColor(COLOR_BTNHIGHLIGHT)); |
| | 228 | CPenHandle oldpen = dc.SelectPen(lightPen); |
| | 229 | dc.MoveTo(0, rc.top); |
| | 230 | dc.LineTo(rc.right, rc.top); |
| | 231 | // dc.Draw3dRect(&rc, GetSysColor(COLOR_BTNHIGHLIGHT), GetSysColor(COLOR_BTNSHADOW));//0, height, width, height+20); |
| 233 | | dc.SetBkMode(prevBkMode); |
| 234 | | dc.SetTextAlign(prevAlign); |
| 235 | | |
| 236 | | selectedPen.DeleteObject(); |
| 237 | | |
| 238 | | // if looping points are set, draw them with protracker-type handles |
| 239 | | int beginLoopX=(float)(beginLoop-beginDisplaySample) / paintDelta; |
| 240 | | int endLoopX=(float)(endLoop-beginDisplaySample) / paintDelta -0.5; |
| 241 | | |
| 242 | | CPen loopPen; |
| 243 | | loopPen.CreatePen(PS_SOLID, 1, 0x00FF00); |
| 244 | | |
| 245 | | dc.SelectPen(loopPen); |
| 246 | | dc.MoveTo(beginLoopX, fullHeight); |
| 247 | | dc.LineTo(beginLoopX, 0); |
| 248 | | dc.LineTo(beginLoopX+8, 0); |
| 249 | | dc.LineTo(beginLoopX+8, 8); |
| 250 | | dc.LineTo(beginLoopX, 8); |
| 251 | | |
| 252 | | dc.MoveTo(endLoopX, fullHeight); |
| 253 | | dc.LineTo(endLoopX, 0); |
| 254 | | dc.LineTo(endLoopX-8, 0); |
| 255 | | dc.LineTo(endLoopX-8, 8); |
| 256 | | dc.LineTo(endLoopX, 8); |
| 257 | | |
| 258 | | return DefWindowProc(); |
| 259 | | } |
| | 277 | dc.SetBkMode(oldBkMode); |
| | 278 | dc.SetTextAlign(oldAlign); |
| | 279 | dc.SelectPen(oldpen); |
| | 280 | dc.SelectFont(oldfont); |
| | 281 | } |
| | 282 | |
| 345 | | |
| 346 | | void CWaveEditorCtrl::setZoomDisplay(size_t beginSample, size_t endSample) { |
| 347 | | |
| 348 | | // update scroll bars |
| 349 | | size_t scrollRange = 0; |
| 350 | | wave_info_ex* wave = getWave(); |
| 351 | | if (wave) { |
| 352 | | size_t sample_count = wave->get_sample_count(getWaveLevel()); |
| 353 | | if (sample_count>0) |
| 354 | | scrollRange = sample_count-1; |
| 355 | | if (endSample >= sample_count) |
| 356 | | endSample = sample_count-1; |
| 357 | | } |
| 358 | | |
| 359 | | beginDisplaySample = beginSample; |
| 360 | | endDisplaySample = endSample; |
| 361 | | |
| 362 | | SetScrollRange(SB_HORZ, 0, scrollRange); |
| | 381 | void CWaveEditorCtrl::updateAlign() { |
| | 382 | RECT rcClient; |
| | 383 | GetClientRect(&rcClient); |
| | 384 | if (rcClient.right > 0) { |
| | 385 | paintDelta = (double)numDisplaySamples / (double)rcClient.right; |
| | 386 | |
| | 387 | if (paintDelta > 1) { |
| | 388 | double modval = fmod(beginDisplaySample, paintDelta); |
| | 389 | const double epsilon = 0.000002; |
| | 390 | if (!compareWithEpsilon(paintDelta, modval, epsilon) && !compareWithEpsilon(modval, (double)0.0f, epsilon)) |
| | 391 | beginDisplaySample -= modval; |
| | 392 | } |
| | 393 | } else |
| | 394 | paintDelta = 0; |
| | 395 | } |
| | 396 | |
| | 397 | void CWaveEditorCtrl::updateScrollbars() { |
| | 398 | |
| | 399 | updateAlign(); |
| | 400 | double samples = waveInfo.samples - 1; |