Merge branch 'individual-channel-export' into hUGETracker
4 changed files
Diffed against the first of 2 parents.
+191−45
1bf039f@@ -1 +1 @@ | |||
| 1 | Subproject commit a3cbd0cea48e6784d7f625066d0300f7cb075926 |
1 | Subproject commit 3200c60c5eec5310105aa406a4e1cd1154875a37 |
@@ -1,11 +1,11 @@ | |||
| 1 | object frmRenderToWave: TfrmRenderToWave |
1 | object frmRenderToWave: TfrmRenderToWave |
| 2 | Left = 638 |
2 | Left = 638 |
| 3 | Height = 279 |
3 | Height = 320 |
| 4 | Top = 453 |
4 | Top = 453 |
| 5 | Width = 601 |
5 | Width = 601 |
| 6 | BorderStyle = bsSingle |
6 | BorderStyle = bsSingle |
| 7 | Caption = 'Render song' |
7 | Caption = 'Render song' |
| 8 | ClientHeight = 279 |
8 | ClientHeight = 320 |
| 9 | ClientWidth = 601 |
9 | ClientWidth = 601 |
| 10 | DesignTimePPI = 128 |
10 | DesignTimePPI = 128 |
| 11 | OnShow = FormShow |
11 | OnShow = FormShow |
@@ -14,7 +14,7 @@ object frmRenderToWave: TfrmRenderToWave | |||
| 14 | object RenderButton: TButton |
14 | object RenderButton: TButton |
| 15 | Left = 19 |
15 | Left = 19 |
| 16 | Height = 33 |
16 | Height = 33 |
| 17 | Top = 220 |
17 | Top = 261 |
| 18 | Width = 352 |
18 | Width = 352 |
| 19 | Caption = 'Render' |
19 | Caption = 'Render' |
| 20 | Enabled = False |
20 | Enabled = False |
@@ -50,7 +50,7 @@ object frmRenderToWave: TfrmRenderToWave | |||
| 50 | object CancelButton: TButton |
50 | object CancelButton: TButton |
| 51 | Left = 380 |
51 | Left = 380 |
| 52 | Height = 33 |
52 | Height = 33 |
| 53 | Top = 220 |
53 | Top = 261 |
| 54 | Width = 195 |
54 | Width = 195 |
| 55 | Caption = 'Cancel' |
55 | Caption = 'Cancel' |
| 56 | Enabled = False |
56 | Enabled = False |
@@ -144,9 +144,18 @@ object frmRenderToWave: TfrmRenderToWave | |||
| 144 | object Panel1: TPanel |
144 | object Panel1: TPanel |
| 145 | Left = 21 |
145 | Left = 21 |
| 146 | Height = 45 |
146 | Height = 45 |
| 147 | Top = 160 |
147 | Top = 201 |
| 148 | Width = 556 |
148 | Width = 556 |
| 149 | Caption = 'Ready' |
149 | Caption = 'Ready' |
| 150 | TabOrder = 9 |
150 | TabOrder = 9 |
| 151 | end |
151 | end |
|
152 | object ExportChannelsCheckBox: TCheckBox |
|
|
153 | Left = 19 |
|
|
154 | Height = 27 |
|
|
155 | Top = 160 |
|
|
156 | Width = 450 |
|
|
157 | Caption = 'Export each channel as a separate file' |
|
|
158 | ParentFont = False |
|
|
159 | TabOrder = 10 |
|
|
160 | end |
|
| 152 | end |
161 | end |
@@ -17,6 +17,7 @@ type | |||
| 17 | |
17 | |
| 18 | TfrmRenderToWave = class(TForm) |
18 | TfrmRenderToWave = class(TForm) |
| 19 | ComboBox1: TComboBox; |
19 | ComboBox1: TComboBox; |
|
20 | ExportChannelsCheckBox: TCheckBox; |
|
| 20 | Label2: TLabel; |
21 | Label2: TLabel; |
| 21 | Label3: TLabel; |
22 | Label3: TLabel; |
| 22 | Panel1: TPanel; |
23 | Panel1: TPanel; |
@@ -42,6 +43,7 @@ type | |||
| 42 | |
43 | |
| 43 | function FilenameToFormatIndex: Integer; |
44 | function FilenameToFormatIndex: Integer; |
| 44 | function GetFFMPEGFormat: String; |
45 | function GetFFMPEGFormat: String; |
|
46 | function CreateFFMPEGProcess(const DestFilename: String): TProcess; |
|
| 45 | procedure UpdateUI; |
47 | procedure UpdateUI; |
| 46 | |
48 | |
| 47 | procedure ExportWaveToFile(Filename: String); |
49 | procedure ExportWaveToFile(Filename: String); |
@@ -130,20 +132,49 @@ begin | |||
| 130 | CancelButton.Enabled := Rendering and not CancelRequested; |
132 | CancelButton.Enabled := Rendering and not CancelRequested; |
| 131 | end; |
133 | end; |
| 132 | |
134 | |
| 133 | procedure TfrmRenderToWave.ExportWaveToFile(Filename: String); |
135 | procedure DrainAndWaitForProcesses(const Procs: array of TProcess); |
| 134 | var |
136 | var |
| 135 | Proc: TProcess; |
137 | AllDone: Boolean; |
|
138 | I, Avail: Integer; |
|
|
139 | Discard: array[0..4095] of Byte; |
|
| 136 | begin |
140 | begin |
| 137 | z80_reset; |
141 | repeat |
| 138 | ResetSound; |
142 | AllDone := True; |
| 139 | enablesound; |
143 | for I := Low(Procs) to High(Procs) do begin |
| 140 | |
144 | if Procs[I] = nil then Continue; |
| 141 | FCCallback := nil; |
145 | |
| 142 | load(ConcatPaths([CacheDir, 'render', 'preview.gb'])); |
146 | if Procs[I].Output <> nil then begin |
|
147 | Avail := Procs[I].Output.NumBytesAvailable; |
|
|
148 | while Avail > 0 do begin |
|
|
149 | if Avail > SizeOf(Discard) then Avail := SizeOf(Discard); |
|
|
150 | Procs[I].Output.Read(Discard, Avail); |
|
|
151 | Avail := Procs[I].Output.NumBytesAvailable; |
|
|
152 | end; |
|
|
153 | end; |
|
|
154 | |
|
|
155 | if Procs[I].Stderr <> nil then begin |
|
|
156 | Avail := Procs[I].Stderr.NumBytesAvailable; |
|
|
157 | while Avail > 0 do begin |
|
|
158 | if Avail > SizeOf(Discard) then Avail := SizeOf(Discard); |
|
|
159 | Procs[I].Stderr.Read(Discard, Avail); |
|
|
160 | Avail := Procs[I].Stderr.NumBytesAvailable; |
|
|
161 | end; |
|
|
162 | end; |
|
|
163 | |
|
|
164 | if Procs[I].Running then AllDone := False; |
|
|
165 | end; |
|
|
166 | if not AllDone then begin |
|
|
167 | Application.ProcessMessages; |
|
|
168 | Sleep(100); |
|
|
169 | end; |
|
|
170 | until AllDone; |
|
|
171 | end; |
|
| 143 | |
172 | |
| 144 | Proc := TProcess.Create(nil); |
173 | function TfrmRenderToWave.CreateFFMPEGProcess(const DestFilename: String): TProcess; |
| 145 | Proc.Executable := 'ffmpeg'; |
174 | begin |
| 146 | with Proc.Parameters do begin |
175 | Result := TProcess.Create(nil); |
|
176 | Result.Executable := 'ffmpeg'; |
|
|
177 | with Result.Parameters do begin |
|
| 147 | // HACK: to prevent ffmpeg from writing to stderr, we disable all output |
178 | // HACK: to prevent ffmpeg from writing to stderr, we disable all output |
| 148 | // This is needed because ffmpeg blocks unless you read what it writes |
179 | // This is needed because ffmpeg blocks unless you read what it writes |
| 149 | Add('-nostats'); |
180 | Add('-nostats'); |
@@ -161,13 +192,54 @@ begin | |||
| 161 | Add('-'); |
192 | Add('-'); |
| 162 | Add('-f'); |
193 | Add('-f'); |
| 163 | Add(GetFFMPEGFormat); |
194 | Add(GetFFMPEGFormat); |
| 164 | Add(Filename); |
195 | Add(DestFilename); |
|
196 | end; |
|
|
197 | Result.Options := [poUsePipes, poNoConsole]; |
|
|
198 | end; |
|
|
199 | |
|
|
200 | procedure TfrmRenderToWave.ExportWaveToFile(Filename: String); |
|
|
201 | const |
|
|
202 | ChannelSuffixes: array[1..4] of String = ('_pulse1', '_pulse2', '_wave', '_noise'); |
|
|
203 | var |
|
|
204 | MixProc: TProcess; |
|
|
205 | ChanProcs: array[1..4] of TProcess; |
|
|
206 | ChanStreams: array[1..4] of TStream; |
|
|
207 | ExportChannels: Boolean; |
|
|
208 | Dir, Base, Ext: String; |
|
|
209 | I: Integer; |
|
|
210 | begin |
|
|
211 | z80_reset; |
|
|
212 | ResetSound; |
|
|
213 | enablesound; |
|
|
214 | |
|
|
215 | FCCallback := nil; |
|
|
216 | load(ConcatPaths([CacheDir, 'render', 'preview.gb'])); |
|
|
217 | |
|
|
218 | ExportChannels := ExportChannelsCheckBox.Checked; |
|
|
219 | for I := 1 to 4 do begin |
|
|
220 | ChanProcs[I] := nil; |
|
|
221 | ChanStreams[I] := nil; |
|
|
222 | end; |
|
|
223 | |
|
|
224 | MixProc := CreateFFMPEGProcess(Filename); |
|
|
225 | MixProc.Execute; |
|
|
226 | |
|
|
227 | if ExportChannels then begin |
|
|
228 | Dir := ExtractFilePath(Filename); |
|
|
229 | Ext := ExtractFileExt(Filename); |
|
|
230 | Base := ChangeFileExt(ExtractFileName(Filename), ''); |
|
|
231 | for I := 1 to 4 do begin |
|
|
232 | ChanProcs[I] := CreateFFMPEGProcess(Dir + Base + ChannelSuffixes[I] + Ext); |
|
|
233 | ChanProcs[I].Execute; |
|
|
234 | ChanStreams[I] := ChanProcs[I].Input; |
|
|
235 | end; |
|
| 165 | end; |
236 | end; |
| 166 | Proc.Options := [poUsePipes, poNoConsole]; |
|
|
| 167 | Proc.Execute; |
|
|
| 168 | |
237 | |
| 169 | try |
238 | try |
| 170 | BeginWritingSoundToStream(Proc.Input); |
239 | if ExportChannels then |
|
240 | BeginWritingChannelsToStreams(MixProc.Input, ChanStreams[1], ChanStreams[2], ChanStreams[3], ChanStreams[4]) |
|
|
241 | else |
|
|
242 | BeginWritingSoundToStream(MixProc.Input); |
|
| 171 | |
243 | |
| 172 | if PlayEntireSongRadioButton.Checked then |
244 | if PlayEntireSongRadioButton.Checked then |
| 173 | RenderEntireSong(PlayEntireSongSpinEdit.Value) |
245 | RenderEntireSong(PlayEntireSongSpinEdit.Value) |
@@ -178,9 +250,16 @@ begin | |||
| 178 | |
250 | |
| 179 | finally |
251 | finally |
| 180 | EndWritingSoundToStream; |
252 | EndWritingSoundToStream; |
| 181 | Proc.CloseInput; |
253 | |
| 182 | Proc.WaitOnExit; |
254 | MixProc.CloseInput; |
| 183 | Proc.Free; |
255 | for I := 1 to 4 do |
|
256 | if ChanProcs[I] <> nil then ChanProcs[I].CloseInput; |
|
|
257 | |
|
|
258 | DrainAndWaitForProcesses([MixProc, ChanProcs[1], ChanProcs[2], ChanProcs[3], ChanProcs[4]]); |
|
|
259 | |
|
|
260 | MixProc.Free; |
|
|
261 | for I := 1 to 4 do |
|
|
262 | if ChanProcs[I] <> nil then ChanProcs[I].Free; |
|
| 184 | |
263 | |
| 185 | Panel1.Caption := 'Ready'; |
264 | Panel1.Caption := 'Ready'; |
| 186 | end; |
265 | end; |
@@ -154,6 +154,7 @@ procedure ResetSound; | |||
| 154 | procedure SoundUpdate(cycles: integer); |
154 | procedure SoundUpdate(cycles: integer); |
| 155 | |
155 | |
| 156 | procedure BeginWritingSoundToStream(Stream: TStream); |
156 | procedure BeginWritingSoundToStream(Stream: TStream); |
|
157 | procedure BeginWritingChannelsToStreams(MixStream, Ch1Stream, Ch2Stream, Ch3Stream, Ch4Stream: TStream); |
|
| 157 | procedure EndWritingSoundToStream; |
158 | procedure EndWritingSoundToStream; |
| 158 | |
159 | |
| 159 | var |
160 | var |
@@ -181,10 +182,12 @@ uses mainloop, vars; | |||
| 181 | const |
182 | const |
| 182 | SampleSize = SizeOf(Single)*2; |
183 | SampleSize = SizeOf(Single)*2; |
| 183 | SampleCycles: LongInt = (8192 * 1024) div PlaybackFrequency; |
184 | SampleCycles: LongInt = (8192 * 1024) div PlaybackFrequency; |
|
185 | StreamFlushBytes = 32768; |
|
| 184 | |
186 | |
| 185 | var |
187 | var |
| 186 | PlayStream: TSDL_AudioDeviceID; |
188 | PlayStream: TSDL_AudioDeviceID; |
| 187 | bufCycles, bufLVal, bufRVal: integer; |
189 | bufCycles: integer; |
|
190 | bufLVals, bufRVals: array[0..4] of Integer; |
|
| 188 | |
191 | |
| 189 | sndBuffer: ^Single; |
192 | sndBuffer: ^Single; |
| 190 | sndBytesWritten: Integer; |
193 | sndBytesWritten: Integer; |
@@ -192,7 +195,9 @@ var | |||
| 192 | lfsr: Cardinal = 0; |
195 | lfsr: Cardinal = 0; |
| 193 | |
196 | |
| 194 | WritingSoundToStream: Boolean; |
197 | WritingSoundToStream: Boolean; |
| 195 | SoundStream: TStream; |
198 | SoundStreams: array[0..4] of TStream; |
|
199 | StreamBuffers: array[0..4] of array[0..StreamFlushBytes-1] of Byte; |
|
|
200 | StreamBufferUsed: array[0..4] of Integer; |
|
| 196 | |
201 | |
| 197 | procedure ResetSound; |
202 | procedure ResetSound; |
| 198 | var |
203 | var |
@@ -213,15 +218,43 @@ begin | |||
| 213 | end; |
218 | end; |
| 214 | end; |
219 | end; |
| 215 | |
220 | |
|
221 | procedure FlushStreamBuffer(Idx: Integer); |
|
|
222 | begin |
|
|
223 | if (SoundStreams[Idx] <> nil) and (StreamBufferUsed[Idx] > 0) then |
|
|
224 | SoundStreams[Idx].Write(StreamBuffers[Idx], StreamBufferUsed[Idx]); |
|
|
225 | StreamBufferUsed[Idx] := 0; |
|
|
226 | end; |
|
|
227 | |
|
| 216 | procedure BeginWritingSoundToStream(Stream: TStream); |
228 | procedure BeginWritingSoundToStream(Stream: TStream); |
|
229 | var |
|
|
230 | I: Integer; |
|
| 217 | begin |
231 | begin |
|
232 | SoundStreams[0] := Stream; |
|
|
233 | for I := 1 to 4 do SoundStreams[I] := nil; |
|
|
234 | for I := 0 to 4 do StreamBufferUsed[I] := 0; |
|
|
235 | WritingSoundToStream := True; |
|
|
236 | end; |
|
|
237 | |
|
|
238 | procedure BeginWritingChannelsToStreams(MixStream, Ch1Stream, Ch2Stream, Ch3Stream, Ch4Stream: TStream); |
|
|
239 | var |
|
|
240 | I: Integer; |
|
|
241 | begin |
|
|
242 | SoundStreams[0] := MixStream; |
|
|
243 | SoundStreams[1] := Ch1Stream; |
|
|
244 | SoundStreams[2] := Ch2Stream; |
|
|
245 | SoundStreams[3] := Ch3Stream; |
|
|
246 | SoundStreams[4] := Ch4Stream; |
|
|
247 | for I := 0 to 4 do StreamBufferUsed[I] := 0; |
|
| 218 | WritingSoundToStream := True; |
248 | WritingSoundToStream := True; |
| 219 | SoundStream := Stream; |
|
|
| 220 | end; |
249 | end; |
| 221 | |
250 | |
| 222 | procedure EndWritingSoundToStream; |
251 | procedure EndWritingSoundToStream; |
|
252 | var |
|
|
253 | I: Integer; |
|
| 223 | begin |
254 | begin |
|
255 | for I := 0 to 4 do FlushStreamBuffer(I); |
|
| 224 | WritingSoundToStream := False; |
256 | WritingSoundToStream := False; |
|
257 | for I := 0 to 4 do SoundStreams[I] := nil; |
|
| 225 | end; |
258 | end; |
| 226 | |
259 | |
| 227 | procedure StartPlayback; |
260 | procedure StartPlayback; |
@@ -277,8 +310,8 @@ begin | |||
| 277 | |
310 | |
| 278 | soundEnable := True; |
311 | soundEnable := True; |
| 279 | bufCycles := 0; |
312 | bufCycles := 0; |
| 280 | bufLVal := 0; |
313 | FillChar(bufLVals, SizeOf(bufLVals), 0); |
| 281 | bufRVal := 0; |
314 | FillChar(bufRVals, SizeOf(bufRVals), 0); |
| 282 | end; |
315 | end; |
| 283 | |
316 | |
| 284 | procedure DisableSound; |
317 | procedure DisableSound; |
@@ -292,35 +325,49 @@ begin | |||
| 292 | soundEnable := False; |
325 | soundEnable := False; |
| 293 | end; |
326 | end; |
| 294 | |
327 | |
| 295 | procedure SoundDoOut(l, r: Integer; cycles: integer); |
328 | procedure SoundDoOut(const ls, rs: array of Integer; cycles: integer); |
| 296 | var |
329 | var |
|
330 | I: Integer; |
|
| 297 | buf: array[0..1] of Single; |
331 | buf: array[0..1] of Single; |
| 298 | begin |
332 | begin |
| 299 | Inc(bufLVal, l * cycles); |
333 | for I := 0 to 4 do begin |
| 300 | Inc(bufRVal, r * cycles); |
334 | Inc(bufLVals[I], ls[I] * cycles); |
|
335 | Inc(bufRVals[I], rs[I] * cycles); |
|
|
336 | end; |
|
| 301 | Inc(bufCycles, cycles); |
337 | Inc(bufCycles, cycles); |
| 302 | if bufCycles >= sampleCycles then |
338 | if bufCycles >= sampleCycles then |
| 303 | begin |
339 | begin |
| 304 | buf[0] := ((bufRVal div sampleCycles) / 512.0); |
|
|
| 305 | buf[1] := ((bufLVal div sampleCycles) / 512.0); |
|
|
| 306 | bufCycles := 0; |
|
|
| 307 | bufLVal := 0; |
|
|
| 308 | bufRVal := 0; |
|
|
| 309 | |
|
|
| 310 | if WritingSoundToStream then begin |
340 | if WritingSoundToStream then begin |
| 311 | SoundStream.Write(buf, SizeOf(Single)*2); |
341 | for I := 0 to 4 do begin |
|
342 | if SoundStreams[I] <> nil then begin |
|
|
343 | buf[0] := ((bufRVals[I] div sampleCycles) / 512.0); |
|
|
344 | buf[1] := ((bufLVals[I] div sampleCycles) / 512.0); |
|
|
345 | Move(buf, StreamBuffers[I][StreamBufferUsed[I]], SampleSize); |
|
|
346 | Inc(StreamBufferUsed[I], SampleSize); |
|
|
347 | if StreamBufferUsed[I] >= StreamFlushBytes then |
|
|
348 | FlushStreamBuffer(I); |
|
|
349 | end; |
|
|
350 | end; |
|
| 312 | end |
351 | end |
| 313 | else begin |
352 | else begin |
|
353 | buf[0] := ((bufRVals[0] div sampleCycles) / 512.0); |
|
|
354 | buf[1] := ((bufLVals[0] div sampleCycles) / 512.0); |
|
| 314 | sndBuffer^ := buf[0]; |
355 | sndBuffer^ := buf[0]; |
| 315 | Inc(sndBuffer); |
356 | Inc(sndBuffer); |
| 316 | sndBuffer^ := buf[1]; |
357 | sndBuffer^ := buf[1]; |
| 317 | Inc(sndBuffer); |
358 | Inc(sndBuffer); |
| 318 | Inc(sndBytesWritten, SampleSize); |
359 | Inc(sndBytesWritten, SampleSize); |
| 319 | end; |
360 | end; |
|
361 | |
|
|
362 | bufCycles := 0; |
|
|
363 | for I := 0 to 4 do begin |
|
|
364 | bufLVals[I] := 0; |
|
|
365 | bufRVals[I] := 0; |
|
|
366 | end; |
|
| 320 | end; |
367 | end; |
| 321 | end; |
368 | end; |
| 322 | |
369 | |
| 323 | procedure SoundOutBits(l, r: Integer; cycles: integer); |
370 | procedure SoundOutBits(const ls, rs: array of Integer; cycles: integer); |
| 324 | var |
371 | var |
| 325 | left: integer; |
372 | left: integer; |
| 326 | begin |
373 | begin |
@@ -329,10 +376,10 @@ begin | |||
| 329 | while bufCycles + cycles > sampleCycles do |
376 | while bufCycles + cycles > sampleCycles do |
| 330 | begin |
377 | begin |
| 331 | left := sampleCycles - bufCycles; |
378 | left := sampleCycles - bufCycles; |
| 332 | SoundDoOut(l, r, left); |
379 | SoundDoOut(ls, rs, left); |
| 333 | Dec(cycles, left); |
380 | Dec(cycles, left); |
| 334 | end; |
381 | end; |
| 335 | SoundDoOut(l, r, cycles); |
382 | SoundDoOut(ls, rs, cycles); |
| 336 | end; |
383 | end; |
| 337 | |
384 | |
| 338 | const |
385 | const |
@@ -374,6 +421,8 @@ var | |||
| 374 | n, stage: integer; |
421 | n, stage: integer; |
| 375 | ls: array[1..4] of Integer = (0, 0, 0, 0); |
422 | ls: array[1..4] of Integer = (0, 0, 0, 0); |
| 376 | rs: array[1..4] of Integer = (0, 0, 0, 0); |
423 | rs: array[1..4] of Integer = (0, 0, 0, 0); |
|
424 | chanLs, chanRs: array[0..4] of Integer; |
|
|
425 | masterL, masterR: Double; |
|
| 377 | l, r: Integer; |
426 | l, r: Integer; |
| 378 | I: Integer; |
427 | I: Integer; |
| 379 | begin |
428 | begin |
@@ -698,14 +747,23 @@ begin | |||
| 698 | SampleBuffers[I].Cursor := (SampleBuffers[I].Cursor + 1) mod SAMPLE_BUFFER_SIZE; |
747 | SampleBuffers[I].Cursor := (SampleBuffers[I].Cursor + 1) mod SAMPLE_BUFFER_SIZE; |
| 699 | end; |
748 | end; |
| 700 | |
749 | |
| 701 | l := Trunc((ls[1] + ls[2] + ls[3] + ls[4]) * (((m_iram[$FF24] and 7)+1) / 8)); |
750 | masterL := ((m_iram[$FF24] and 7) + 1) / 8; |
| 702 | r := Trunc((rs[1] + rs[2] + rs[3] + rs[4]) * ((((m_iram[$FF24] shr 4) and 7)+1) / 8)); |
751 | masterR := (((m_iram[$FF24] shr 4) and 7) + 1) / 8; |
|
752 | |
|
|
753 | l := Trunc((ls[1] + ls[2] + ls[3] + ls[4]) * masterL); |
|
|
754 | r := Trunc((rs[1] + rs[2] + rs[3] + rs[4]) * masterR); |
|
| 703 | |
755 | |
| 704 | SampleBuffers[0].BufferL[SampleBuffers[0].Cursor] := l; |
756 | SampleBuffers[0].BufferL[SampleBuffers[0].Cursor] := l; |
| 705 | SampleBuffers[0].BufferR[SampleBuffers[0].Cursor] := r; |
757 | SampleBuffers[0].BufferR[SampleBuffers[0].Cursor] := r; |
| 706 | SampleBuffers[0].Cursor := (SampleBuffers[0].Cursor + 1) mod SAMPLE_BUFFER_SIZE; |
758 | SampleBuffers[0].Cursor := (SampleBuffers[0].Cursor + 1) mod SAMPLE_BUFFER_SIZE; |
| 707 | |
759 | |
| 708 | SoundOutBits(l, r, cycles); |
760 | chanLs[0] := l; |
|
761 | chanRs[0] := r; |
|
|
762 | for I := 1 to 4 do begin |
|
|
763 | chanLs[I] := Trunc(ls[I] * masterL); |
|
|
764 | chanRs[I] := Trunc(rs[I] * masterR); |
|
|
765 | end; |
|
|
766 | SoundOutBits(chanLs, chanRs, cycles); |
|
| 709 | end; |
767 | end; |
| 710 | |
768 | |
| 711 | begin |
769 | begin |