Rev 1376 | Rev 1486 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed
Rev | Author | Line No. | Line |
---|---|---|---|
1352 | lee | 1 | // $Id: app.c 1169 2011-04-09 23:31:28Z tk $ |
2 | /* |
||
3 | * MIOS32 SD card polyphonic sample player |
||
4 | * |
||
5 | * ========================================================================== |
||
6 | * |
||
7 | * Copyright (C) 2011 Lee O'Donnell (lee@bassmaker.co.uk) |
||
8 | * Base software Copyright (C) 2009 Thorsten Klose (tk@midibox.org) |
||
9 | * Licensed for personal non-commercial use only. |
||
10 | * All other rights reserved. |
||
11 | * |
||
12 | * ========================================================================== |
||
13 | */ |
||
14 | |||
15 | |||
16 | ///////////////////////////////////////////////////////////////////////////// |
||
17 | // Include files |
||
18 | ///////////////////////////////////////////////////////////////////////////// |
||
19 | |||
20 | #include <mios32.h> |
||
21 | #include "app.h" |
||
22 | #include <file.h> |
||
23 | #include <string.h> |
||
24 | |||
1366 | lee | 25 | // Task stuff - the bank switch scanning is lower priority than the voice processing |
26 | #define PRIORITY_VOICE_TASK ( tskIDLE_PRIORITY + 3 ) |
||
27 | #define PRIORITY_BANKSWITCH_TASK ( tskIDLE_PRIORITY + 2 ) |
||
28 | static void TASK_VOICE_SCAN(void *pvParameters); |
||
29 | static void TASK_BANKSWITCH_SCAN(void *pvParameters); |
||
30 | |||
1352 | lee | 31 | ///////////////////////////////////////////////////////////////////////////// |
32 | // Local definitions |
||
33 | ///////////////////////////////////////////////////////////////////////////// |
||
34 | |||
35 | #define NUM_SAMPLES_TO_OPEN 64 // Maximum number of file handles to use, and how many samples to open |
||
36 | #define POLYPHONY 8 // Max voices to sound simultaneously |
||
1363 | lee | 37 | |
38 | // Following accounts for: 7 bits (envelope decay) + 7 bits (velocity related volume) + 1-3 bits (mixing up to 8 samples but depends how hot your samples are) |
||
1354 | lee | 39 | #define SAMPLE_SCALING 8 // Number of bits to scale samples down by in order to not distort |
1363 | lee | 40 | |
1357 | lee | 41 | #define MAX_TIME_IN_DMA 45 // Time (*0.1ms) to read sample data for, e.g. 40 = 4.0 mS |
1352 | lee | 42 | |
43 | #define SAMPLE_BUFFER_SIZE 512 // -> 512 L/R samples, 80 Hz refill rate (11.6~ mS period). DMA refill routine called every 5.8mS. |
||
44 | // NB sample rate and SPI prescaler set in mios32_config file - at 44.1kHz, reading 2 bytes per sample is SD card average rate of 86.13kB/s for a single sample |
||
45 | |||
46 | #define DEBUG_VERBOSE_LEVEL 10 |
||
47 | #define DEBUG_MSG MIOS32_MIDI_SendDebugMessage |
||
48 | |||
1357 | lee | 49 | // Now mandatory to have this set as legacy read code removed |
50 | #define CLUSTER_CACHE_SIZE 32 // typically for 32 * 64*512 bytes = max sample file length of 1 MB !!! |
||
1356 | tk | 51 | |
1357 | lee | 52 | // set to 1 to perform right channel inversion for PCM1725 DAC |
53 | #define DAC_FIX 0 |
||
54 | |||
1371 | lee | 55 | // name of config file on SD card to read |
56 | #define CONFIG_FNAME "player.cfg" |
||
57 | |||
1352 | lee | 58 | ///////////////////////////////////////////////////////////////////////////// |
59 | // Local Variables |
||
60 | ///////////////////////////////////////////////////////////////////////////// |
||
61 | |||
62 | ///////////////////////////////////////////////////////////////////////////// |
||
63 | // Global Variables |
||
64 | ///////////////////////////////////////////////////////////////////////////// |
||
65 | |||
66 | // All filenames are supported in 8.3 format |
||
67 | |||
1371 | lee | 68 | u8 lee_hw=0; // set to enable scanning of Lee's temporary bank switch on J10 |
69 | u8 midichannel=1; // MIDI channel to respond to |
||
70 | |||
1366 | lee | 71 | static u8 voice_no=0; // used to count number of voices to play |
72 | static u8 voice_samples[POLYPHONY]; // Store which sample numbers are playing in which voice |
||
73 | static s16 voice_velocity[POLYPHONY]; // Store the velocity for each sample |
||
74 | |||
1355 | lee | 75 | char bankprefix[13]="bank."; // Default sample bank filename prefix on the SD card, needs to have Unix style line feeds |
1352 | lee | 76 | static file_t bank_fileinfo; // Create the file descriptor for bank file |
77 | |||
78 | int sample_to_midinote[NUM_SAMPLES_TO_OPEN]; // Stores midi note mappings from bank file |
||
79 | u8 no_samples_loaded; // Stores number of samples we read in from bank file (and will scan for activity) |
||
80 | |||
81 | static u32 sample_buffer[SAMPLE_BUFFER_SIZE]; // sample buffer used for DMA |
||
82 | |||
83 | static u32 samplefile_pos[NUM_SAMPLES_TO_OPEN]; // Current position in the sample file |
||
84 | static u32 samplefile_len[NUM_SAMPLES_TO_OPEN]; // Length of the sample file |
||
1363 | lee | 85 | static s16 sample_on[NUM_SAMPLES_TO_OPEN]; // To track whether each sample should be on or not |
86 | static s8 sample_vel[NUM_SAMPLES_TO_OPEN]; // Sample velocity |
||
87 | static u8 sample_decay[NUM_SAMPLES_TO_OPEN]; // Sample decay parameter (used to calculate per sample, based on velocity the decrement value) |
||
88 | static u8 sample_decval[NUM_SAMPLES_TO_OPEN]; // Decay time read in from bank file |
||
89 | static u8 no_decay; // Used to speed up decay routine if this bank has no decay time |
||
90 | static u8 hold_sample[NUM_SAMPLES_TO_OPEN]; // Used to hold sample (for drums) |
||
1352 | lee | 91 | static file_t samplefile_fileinfo[NUM_SAMPLES_TO_OPEN]; // Create the right number of file descriptors |
92 | static u8 samplebyte_buf[POLYPHONY][SAMPLE_BUFFER_SIZE]; // Create a buffer for each voice |
||
1357 | lee | 93 | static u32 sample_cluster_cache[NUM_SAMPLES_TO_OPEN][CLUSTER_CACHE_SIZE]; // Array of sample cluster positions on SD card |
1352 | lee | 94 | |
1366 | lee | 95 | static u8 sample_bank_no=1; // The sample bank number being played |
1370 | lee | 96 | static u8 damper_pedal=0; // Damper pedal on channel 1 |
1356 | tk | 97 | |
1352 | lee | 98 | volatile u8 print_msg; |
99 | |||
1358 | lee | 100 | static u8 sdcard_access_allowed=0; // allow SD Card access for SYNTH_ReloadSampleBuffer |
1353 | tk | 101 | |
1362 | lee | 102 | // Curve to map velocity to volume of samples |
103 | static const u8 velocity_curve[128] = { |
||
104 | |||
105 | 36 ,37 ,39 ,41 ,43 ,45 ,46 ,48 ,50 ,51 ,53 ,55 ,56 ,58 ,59 ,61 , |
||
106 | 62 ,63 ,65 ,66 ,68 ,69 ,70 ,71 ,73 ,74 ,75 ,76 ,78 ,79 ,80 ,81 , |
||
107 | 82 ,83 ,84 ,85 ,86 ,87 ,88 ,89 ,90 ,91 ,92 ,93 ,93 ,94 ,94 ,95 , |
||
108 | 96 ,97 ,98 ,99 ,100,101,101,102,103,103,104,105,105,106,107,107, |
||
109 | 108,109,109,110,110,111,111,112,112,113,113,114,114,115,115,116, |
||
110 | 116,117,117,118,118,118,119,119,120,120,120,121,121,121,122,122, |
||
111 | 122,123,123,123,124,124,124,125,125,125,126,126,126,127,127,127 |
||
112 | }; |
||
1353 | tk | 113 | |
1362 | lee | 114 | |
1352 | lee | 115 | // Call this routine with the sample array number to reference, and the filename to open |
116 | s32 SAMP_FILE_open(u8 sample_n, char fname[]) |
||
117 | { |
||
1409 | lee | 118 | DEBUG_MSG("Filename is %s.",fname); |
1352 | lee | 119 | s32 status = FILE_ReadOpen(&samplefile_fileinfo[sample_n], fname); |
120 | FILE_ReadClose(&samplefile_fileinfo[sample_n]); // close again - file will be reopened by read handler |
||
121 | |||
122 | if( status < 0 ) { |
||
123 | DEBUG_MSG("[APP] failed to open file, status: %d\n", status); |
||
124 | } else { |
||
125 | |||
126 | // got it |
||
127 | samplefile_pos[sample_n] = 0; |
||
128 | samplefile_len[sample_n] = samplefile_fileinfo[sample_n].fsize; |
||
129 | |||
130 | DEBUG_MSG("[APP] Sample no %d filename %s opened of length %u\n", sample_n,fname,samplefile_len[sample_n]); |
||
131 | } |
||
132 | |||
133 | return status; |
||
134 | } |
||
135 | |||
136 | ///////////////////////////////////////////////////////////////////////////// |
||
137 | // reads <len> bytes from the .mid file into <buffer> |
||
138 | // returns number of read bytes |
||
139 | ///////////////////////////////////////////////////////////////////////////// |
||
1366 | lee | 140 | int SAMP_FILE_read(void *buffer, u32 len, u8 sample_n) |
1352 | lee | 141 | { |
1356 | tk | 142 | // determine sector based on sample position |
143 | u32 pos = samplefile_pos[sample_n]; |
||
144 | u32 sector_ix = pos / 512; |
||
145 | u32 sectors_per_cluster = FILE_VolumeSectorsPerCluster(); |
||
146 | u32 cluster_ix = sector_ix / sectors_per_cluster; |
||
147 | if( cluster_ix >= CLUSTER_CACHE_SIZE ) |
||
148 | return -1; |
||
1366 | lee | 149 | |
1356 | tk | 150 | u32 cluster = sample_cluster_cache[sample_n][cluster_ix]; |
151 | u32 phys_sector = FILE_VolumeCluster2Sector(cluster) + (sector_ix % sectors_per_cluster); |
||
152 | if( MIOS32_SDCARD_SectorRead(phys_sector, buffer) < 0 ) |
||
153 | return -2; |
||
154 | return len; |
||
1352 | lee | 155 | } |
156 | |||
1357 | lee | 157 | void Open_Bank(u8 b_num) // Open the bank number passed and parse the bank information, load samples, set midi notes, number of samples and cache cluster positions |
1352 | lee | 158 | { |
159 | u8 samp_no; |
||
1409 | lee | 160 | u8 f_line[63]; // 0..3=0xXX (hex midi note) 4=space 5=sample hold (0 or 1) 6=space 7..10=decay (4 digit decimal) 11=space 12..23=8.3 filename 24=null |
1355 | lee | 161 | char b_file[13]; // Overall bank name to generate |
162 | char b_num_char[4]; // Up to 3 digit bank string plus terminator |
||
1409 | lee | 163 | static char sample_filenames[NUM_SAMPLES_TO_OPEN][30]; // Stores sample mappings from bank file, needs to be static to avoid crash |
1355 | lee | 164 | |
165 | strcpy(b_file,bankprefix); // Get prefix in |
||
166 | sprintf(b_num_char,"%d",b_num); // get bank number as string |
||
167 | strcat(b_file,b_num_char); // Create the final filename |
||
168 | |||
1358 | lee | 169 | MIOS32_BOARD_LED_Set(0x1, 0x1); // Turn on LED during bank load |
1355 | lee | 170 | |
1358 | lee | 171 | no_samples_loaded=0; |
1363 | lee | 172 | no_decay=1; // Default to no decay for bank |
1358 | lee | 173 | |
1352 | lee | 174 | DEBUG_MSG("Opening bank file %s",b_file); |
175 | if(FILE_ReadOpen(&bank_fileinfo, b_file)<0) { DEBUG_MSG("Failed to open bank file."); } |
||
1409 | lee | 176 | else |
1352 | lee | 177 | { |
1409 | lee | 178 | for(samp_no=0;samp_no<NUM_SAMPLES_TO_OPEN;samp_no++) // Check for up to the defined maximum of sample mappings (one per line) |
179 | { |
||
180 | if(FILE_ReadLine(f_line, 63)) // Read line up to 63 chars long |
||
181 | { |
||
182 | //DEBUG_MSG("Sample no %d, Line is: %s",samp_no,f_line); |
||
183 | sample_to_midinote[samp_no]=(int)strtol((char *)(f_line+2),NULL,16); // Convert hex string values to a real number (pos 2 on line, base 16) |
||
184 | hold_sample[samp_no]=(int)strtol((char *)(f_line+5),NULL,10); // Convert sample hold digit |
||
185 | sample_decval[samp_no]=(int)strtol((char *)(f_line+7),NULL,10); // Convert decay number (pos 5 on line, base 10) |
||
186 | if(sample_decval[samp_no]>0) { no_decay=0; } // At least one of the samples requires decay processing |
||
187 | (void) strncpy(sample_filenames[samp_no],(char *)(f_line+12),30); // Put name into array of sample names (pos 10 on line), up to 12 chars (8.3) |
||
188 | DEBUG_MSG("Sample no %d, filename is: %s, midi note value=0x%x, decay value %d, hold=%d",samp_no,sample_filenames[samp_no],sample_to_midinote[samp_no],sample_decval[samp_no],hold_sample[samp_no]); |
||
189 | no_samples_loaded++; // increment global number of samples we will read in and scan for in play |
||
190 | } |
||
191 | } |
||
192 | FILE_ReadClose(&bank_fileinfo); |
||
193 | |||
194 | for(samp_no=0;samp_no<no_samples_loaded;samp_no++) // Open all sample files and mark all samples as off |
||
195 | { |
||
196 | if(SAMP_FILE_open(samp_no,sample_filenames[samp_no])) { |
||
197 | DEBUG_MSG("Open sample file failed."); |
||
198 | } else { |
||
199 | // Pre-read all the cluster positions for all samples to open |
||
200 | u32 num_sectors_per_cluster = FILE_VolumeSectorsPerCluster(); |
||
201 | u32 cluster_ix; |
||
202 | for(cluster_ix=0; cluster_ix < CLUSTER_CACHE_SIZE; ++cluster_ix) { |
||
203 | u32 pos = cluster_ix*num_sectors_per_cluster*SAMPLE_BUFFER_SIZE; |
||
1356 | tk | 204 | |
1409 | lee | 205 | if( pos >= samplefile_len[samp_no] ) |
206 | break; // end of file reached |
||
207 | else { |
||
208 | s32 status; |
||
209 | if( (status=FILE_ReadReOpen(&samplefile_fileinfo[samp_no])) >= 0 ) { |
||
210 | status = FILE_ReadSeek(pos); |
||
211 | if( status >= 0 ) { |
||
212 | u8 dummy; // dummy read to update cluster |
||
213 | status = FILE_ReadBuffer(&dummy, 1); |
||
214 | } |
||
215 | FILE_ReadClose(&samplefile_fileinfo[samp_no]); |
||
216 | } |
||
217 | if( status < 0 ) |
||
218 | break; |
||
219 | } |
||
1356 | tk | 220 | |
1409 | lee | 221 | sample_cluster_cache[samp_no][cluster_ix] = samplefile_fileinfo[samp_no].curr_clust; |
222 | DEBUG_MSG("Cluster %d: %d ", cluster_ix, sample_cluster_cache[samp_no][cluster_ix]); |
||
223 | } |
||
224 | } |
||
1356 | tk | 225 | |
1409 | lee | 226 | sample_on[samp_no]=0; // Set sample to off |
227 | } |
||
228 | } |
||
229 | MIOS32_BOARD_LED_Set(0x1, 0x0); // Turn off LED after bank load |
||
1352 | lee | 230 | } |
231 | |||
1409 | lee | 232 | u8 Read_Switch(void) // Lee's temp hardware: Set up inputs for bank switch as input with pullups, and find if any lines pulled low to select bank |
1355 | lee | 233 | { |
1409 | lee | 234 | u8 pin_no; |
235 | u8 bank_val; |
||
1355 | lee | 236 | |
1409 | lee | 237 | for(pin_no=0;pin_no<8;pin_no++) |
238 | { |
||
239 | MIOS32_BOARD_J10_PinInit(pin_no, MIOS32_BOARD_PIN_MODE_INPUT_PU); |
||
240 | } |
||
1355 | lee | 241 | |
1409 | lee | 242 | bank_val=(u8)MIOS32_BOARD_J10_Get(); // Read all pins, if all pins high, val=0 meaning bank 1, otherwise one pin should be pulled low eg bank_index 0 = bank_val=1 so bank 2 |
243 | if(bank_val==127) { return 9; } // D7 = 128 low |
||
244 | if(bank_val==191) { return 8; } // D6 = 64 low |
||
245 | if(bank_val==223) { return 7; } // D5 = 32 low |
||
246 | if(bank_val==239) { return 6; } // D4 = 16 low |
||
247 | if(bank_val==247) { return 5; } // D3 = 8 low |
||
248 | if(bank_val==251) { return 4; } // D2 = 4 low |
||
249 | if(bank_val==253) { return 3; } // D1 = 2 low |
||
250 | if(bank_val==254) { return 2; } // D0 = 1 low |
||
251 | return 1; // default to bank 1 (bank val 255) |
||
1355 | lee | 252 | } |
253 | |||
1371 | lee | 254 | void Read_Config() // Open the config file on the SD card and (re)set various settings for the player |
255 | { |
||
256 | u8 f_line[25]; // parameter and value space separated |
||
257 | char *param_name, *param_value; // |
||
258 | static file_t config_fileinfo; // Create the file descriptor for config file |
||
259 | |||
260 | DEBUG_MSG("Opening config file %s",CONFIG_FNAME); |
||
261 | if(FILE_ReadOpen(&config_fileinfo, CONFIG_FNAME)<0) { DEBUG_MSG("Failed to open config file."); return; } |
||
262 | |||
263 | while(1) // keep going until we get an EOF |
||
264 | { |
||
265 | if(FILE_ReadLine(f_line, 25)) // Read line up to 24 chars long |
||
266 | { |
||
267 | //DEBUG_MSG("Config Line is: %s",f_line); |
||
268 | param_name=strtok((char *)f_line, " "); // find param name up to the space |
||
269 | param_value=strtok(NULL,"\n"); // find value |
||
270 | DEBUG_MSG("Found param %s and value %s",param_name,param_value); |
||
271 | if(!strcmp(param_name,"lee_hw")) { lee_hw=(int)strtol((char *)(param_value),NULL,10); } // Set lee_hw param |
||
272 | if(!strcmp(param_name,"midichannel")) { midichannel=((int)strtol((char *)(param_value),NULL,10)-1); } // Set midichannel (midi receive routines need -1 from decimal value) |
||
273 | } |
||
274 | else { break; } // Hit EOF |
||
275 | } |
||
276 | FILE_ReadClose(&config_fileinfo); |
||
277 | } |
||
278 | |||
1352 | lee | 279 | ///////////////////////////////////////////////////////////////////////////// |
280 | // This hook is called after startup to initialize the application |
||
281 | ///////////////////////////////////////////////////////////////////////////// |
||
282 | void APP_Init(void) |
||
283 | { |
||
284 | // initialize all LEDs |
||
285 | MIOS32_BOARD_LED_Init(0xffffffff); |
||
286 | |||
287 | // print first message |
||
288 | print_msg = PRINT_MSG_INIT; |
||
289 | DEBUG_MSG(MIOS32_LCD_BOOT_MSG_LINE1); |
||
290 | DEBUG_MSG(MIOS32_LCD_BOOT_MSG_LINE2); |
||
291 | DEBUG_MSG("Initialising SD card.."); |
||
292 | |||
293 | if(FILE_Init(0)<0) { DEBUG_MSG("Error initialising SD card"); } // initialise SD card |
||
294 | |||
1376 | tk | 295 | // wait until SD Card available |
296 | int timeout_ctr; |
||
297 | for(timeout_ctr=0; timeout_ctr<1000; ++timeout_ctr) |
||
298 | if( MIOS32_SDCARD_CheckAvailable(0) >= 1 ) |
||
299 | break; |
||
300 | |||
301 | if( timeout_ctr == 1000 ) { |
||
302 | DEBUG_MSG("SD Card error: connection failed!"); |
||
303 | } |
||
304 | |||
1352 | lee | 305 | //s32 status=FILE_PrintSDCardInfos(); // Print SD card info |
1371 | lee | 306 | |
307 | // Read config file from SD card |
||
308 | Read_Config(); |
||
1352 | lee | 309 | |
310 | // Open bank file |
||
311 | |||
1371 | lee | 312 | if (lee_hw) // If we have non standard bank switch connected |
313 | { |
||
314 | DEBUG_MSG("Reading J10 switch"); |
||
315 | sample_bank_no=Read_Switch(); // For Lee's temporary bank physical switch on J10 - read first bank to load on boot |
||
316 | } |
||
317 | |||
1357 | lee | 318 | Open_Bank(sample_bank_no); // Open default bank on boot (1 if Lee's switch not read) |
319 | |||
1352 | lee | 320 | DEBUG_MSG("Initialising synth..."); |
1357 | lee | 321 | |
322 | // allow SD Card access |
||
323 | sdcard_access_allowed = 1; |
||
324 | |||
325 | // init Synth |
||
1352 | lee | 326 | SYNTH_Init(0); |
327 | DEBUG_MSG("Synth init done."); |
||
1353 | tk | 328 | |
1352 | lee | 329 | MIOS32_STOPWATCH_Init(100); // Use stopwatch in 100uS accuracy |
1366 | lee | 330 | |
331 | // Start tasks for voice processing and bank switch scanning |
||
332 | xTaskCreate(TASK_VOICE_SCAN, (signed portCHAR *)"VOICE_SCAN", configMINIMAL_STACK_SIZE, NULL, PRIORITY_VOICE_TASK, NULL); |
||
333 | xTaskCreate(TASK_BANKSWITCH_SCAN, (signed portCHAR *)"BANKSWITCH_SCAN", configMINIMAL_STACK_SIZE, NULL, PRIORITY_BANKSWITCH_TASK, NULL); |
||
1352 | lee | 334 | } |
335 | |||
336 | ///////////////////////////////////////////////////////////////////////////// |
||
337 | // This hook is called when a MIDI package has been received |
||
338 | ///////////////////////////////////////////////////////////////////////////// |
||
339 | void APP_MIDI_NotifyPackage(mios32_midi_port_t port, mios32_midi_package_t midi_package) |
||
340 | { |
||
341 | u8 samp_no; |
||
342 | |||
1371 | lee | 343 | if( midi_package.chn == midichannel && (midi_package.type == NoteOn || midi_package.type == NoteOff) ) // Only interested in note on/off on chn1 |
1352 | lee | 344 | { |
345 | if( midi_package.event == NoteOn && midi_package.velocity > 0 ) |
||
346 | { |
||
347 | for(samp_no=0;samp_no<no_samples_loaded;samp_no++) // go through array looking for mapped notes |
||
348 | { |
||
349 | if(midi_package.note==sample_to_midinote[samp_no]) // Midi note on matches a note mapped to this sample samp_no |
||
350 | { |
||
1363 | lee | 351 | sample_on[samp_no]=-1; // Retrigger the sample |
1362 | lee | 352 | sample_vel[samp_no]=velocity_curve[midi_package.velocity]; |
1363 | lee | 353 | // Mark it as want to play unless it's already on |
1352 | lee | 354 | //DEBUG_MSG("Turning sample %d on , midi note %x hex",samp_no,midi_package.note); |
355 | } |
||
356 | } |
||
357 | //} |
||
358 | } |
||
359 | else // We have a note off |
||
360 | { |
||
1363 | lee | 361 | for(samp_no=0;samp_no<no_samples_loaded;samp_no++) // go through array looking for mapped notes |
1352 | lee | 362 | { |
1370 | lee | 363 | if (!hold_sample[samp_no] && !damper_pedal) // if not holding sample or damper pedal not pressed, turn the note off, otherwise do nothing |
1352 | lee | 364 | { |
1363 | lee | 365 | if(midi_package.note==sample_to_midinote[samp_no]) // Midi note on matches a note mapped to this sample samp_no |
366 | { |
||
367 | if(no_decay || sample_decval[samp_no]==0) { sample_on[samp_no]=0; } // Turn off immediately if no decay for bank or this sample |
||
368 | else |
||
369 | { |
||
370 | sample_on[samp_no]=sample_decval[samp_no]; // Mark it as decaying with the appropriate time for this sample |
||
371 | sample_decay[samp_no]=1+sample_vel[samp_no]/((sample_decval[samp_no]>>3)+1); // Amount to decay by each 8th time around the DMA routine (big = fast decay) |
||
372 | } |
||
373 | //DEBUG_MSG("Turning sample %d off, midi note %x hex",samp_no,midi_package.note); |
||
374 | } |
||
1352 | lee | 375 | } |
376 | } |
||
377 | } |
||
378 | } |
||
1371 | lee | 379 | else if (midi_package.chn==midichannel && midi_package.type==ProgramChange) |
1370 | lee | 380 | { |
381 | sample_bank_no=midi_package.evnt1; // Set new bank |
||
382 | DEBUG_MSG("MIDI Program Change received - Changing bank to %d",sample_bank_no); |
||
383 | sdcard_access_allowed=0; |
||
384 | DEBUG_MSG("Opening new sample bank"); |
||
385 | Open_Bank(sample_bank_no); // Load relevant bank |
||
386 | sdcard_access_allowed=1; |
||
387 | } |
||
1352 | lee | 388 | else |
389 | { |
||
1370 | lee | 390 | //DEBUG_MSG("Other MIDI message received, channel %d, event %X, type %X, cc number %d, value %d... ignoring.",midi_package.chn,midi_package.event,midi_package.type,midi_package.cc_number,midi_package.value); |
1352 | lee | 391 | } |
392 | } |
||
393 | |||
394 | ///////////////////////////////////////////////////////////////////////////// |
||
395 | // This hook is called before the shift register chain is scanned |
||
396 | ///////////////////////////////////////////////////////////////////////////// |
||
397 | void APP_SRIO_ServicePrepare(void) |
||
398 | { |
||
399 | } |
||
400 | |||
401 | ///////////////////////////////////////////////////////////////////////////// |
||
402 | // This hook is called after the shift register chain has been scanned |
||
403 | ///////////////////////////////////////////////////////////////////////////// |
||
404 | void APP_SRIO_ServiceFinish(void) |
||
405 | { |
||
406 | } |
||
407 | |||
408 | ///////////////////////////////////////////////////////////////////////////// |
||
409 | // This hook is called when a button has been toggled |
||
410 | // pin_value is 1 when button released, and 0 when button pressed |
||
411 | ///////////////////////////////////////////////////////////////////////////// |
||
412 | void APP_DIN_NotifyToggle(u32 pin, u32 pin_value) |
||
413 | { |
||
414 | } |
||
415 | |||
416 | ///////////////////////////////////////////////////////////////////////////// |
||
417 | // This function is called by MIOS32_I2S when the lower (state == 0) or |
||
418 | // upper (state == 1) range of the sample buffer has been transfered, so |
||
419 | // that it can be updated |
||
420 | ///////////////////////////////////////////////////////////////////////////// |
||
421 | void SYNTH_ReloadSampleBuffer(u32 state) |
||
422 | { |
||
1360 | lee | 423 | // transfer new samples to the lower/upper sample buffer range |
424 | int i; |
||
425 | u32 *buffer = (u32 *)&sample_buffer[state ? (SAMPLE_BUFFER_SIZE/2) : 0]; // point at either 0 or the upper half of buffer |
||
1352 | lee | 426 | |
1360 | lee | 427 | if( !sdcard_access_allowed ) // no access allowed by main thread |
428 | { |
||
429 | for(i=0; i<SAMPLE_BUFFER_SIZE; i+=2) { // Fill half the sample buffer with silence |
||
430 | *buffer++ = 0; // Muted output |
||
431 | } |
||
432 | return; |
||
433 | } |
||
1353 | tk | 434 | |
1352 | lee | 435 | // Each sample buffer entry contains the L/R 32 bit values |
436 | // Each call of this routine will need to read in SAMPLE_BUFFER_SIZE/2 samples, each of which requires 16 bits |
||
437 | // Therefore for mono samples, we'll need to read in SAMPLE_BUFFER_SIZE bytes |
||
438 | |||
1366 | lee | 439 | u8 voice; |
440 | |||
1352 | lee | 441 | s16 OutWavs16; // 16 bit output to DAC |
442 | s32 OutWavs32; // 32 bit accumulator to mix samples into |
||
443 | u32 ms_so_far; // used to measure time of DMA routine |
||
444 | |||
445 | MIOS32_STOPWATCH_Reset(); // Reset the stopwatch at start of DMA routine |
||
446 | MIOS32_BOARD_LED_Set(0x1, 0x1); // Turn on LED at start of DMA routine |
||
447 | |||
448 | |||
449 | // Here we have voice_no samples to play simultaneously, and the samples contained in voice_samples array |
||
450 | |||
451 | if(voice_no) // if there's anything to play, read the samples and mix otherwise output silence |
||
452 | { |
||
453 | for(voice=0;voice<voice_no;voice++) // read up to SAMPLE_BUFFER_SIZE characters into buffer for each voice |
||
454 | { |
||
1363 | lee | 455 | if(SAMP_FILE_read(samplebyte_buf[voice],SAMPLE_BUFFER_SIZE,voice_samples[voice])<0) // Read in the appropriate number of sectors for each sample thats on |
456 | { // if <0 then there was an error reading, so turn this sample off and mute it |
||
457 | sample_on[voice_samples[voice]]=0; // Turn sample off |
||
1366 | lee | 458 | voice_velocity[voice]=0; // Silence it in the mix as we don't have a complete buffer |
1363 | lee | 459 | } |
460 | |||
1352 | lee | 461 | samplefile_pos[voice_samples[voice]]+=SAMPLE_BUFFER_SIZE; // Move along the file position by the read buffer size |
1357 | lee | 462 | if(samplefile_pos[voice_samples[voice]] >= samplefile_len[voice_samples[voice]]) // We've reached EOF - don't play this sample next time and also free up the voice |
1352 | lee | 463 | { |
1363 | lee | 464 | sample_on[voice_samples[voice]]=0; // Turn sample off |
465 | //DEBUG_MSG("Reached EOF on sample %d",voice_samples[voice]); |
||
1357 | lee | 466 | } |
1352 | lee | 467 | ms_so_far= MIOS32_STOPWATCH_ValueGet(); // Check how long we've been in the routine up until this point |
468 | if(ms_so_far>MAX_TIME_IN_DMA) |
||
469 | { |
||
470 | DEBUG_MSG("Abandoning DMA routine after %d.%d ms, after %d voices out of %d",ms_so_far/10,ms_so_far%10,voice+1,voice_no); |
||
471 | voice_no=voice+1; // don't mix un-read voices (eg if break after 1st voice, voice_no should =1) |
||
472 | break; // go straight to sample mixing |
||
473 | } |
||
474 | } |
||
475 | |||
476 | for(i=0; i<SAMPLE_BUFFER_SIZE; i+=2) // Fill half the sample buffer |
||
477 | { |
||
478 | OutWavs32=0; // zero the voice accumulator for this sample output |
||
479 | for(voice=0;voice<voice_no;voice++) |
||
480 | { |
||
1354 | lee | 481 | OutWavs32+=voice_velocity[voice]*(s16)((samplebyte_buf[voice][i+1] << 8) + samplebyte_buf[voice][i]); // else mix it in |
1352 | lee | 482 | } |
1357 | lee | 483 | OutWavs32 = (OutWavs32>>SAMPLE_SCALING); // Round down the wave to prevent distortion, and factor in the velocity multiply |
484 | if(OutWavs32>32767) { OutWavs32=32767; } // Saturate positive |
||
485 | if(OutWavs32<-32768) { OutWavs32=-32768; } // Saturate negative |
||
486 | OutWavs16 = (s16)OutWavs32; // Required to make following bit shift work correctly including sign bit |
||
487 | #if DAC_FIX |
||
488 | *buffer++ = (OutWavs16 << 16) | (-OutWavs16 & 0xffff); // make up the 32 bit word for L and R and write into buffer with fix for PCM1725 DAC |
||
489 | #else |
||
1352 | lee | 490 | *buffer++ = (OutWavs16 << 16) | (OutWavs16 & 0xffff); // make up the 32 bit word for L and R and write into buffer |
1357 | lee | 491 | #endif |
492 | } |
||
1352 | lee | 493 | } |
494 | else // There were no voices on |
||
495 | { |
||
496 | for(i=0; i<SAMPLE_BUFFER_SIZE; i+=2) { // Fill half the sample buffer with silence |
||
497 | *buffer++ = 0; // Muted output |
||
498 | } |
||
499 | } |
||
500 | |||
501 | MIOS32_BOARD_LED_Set(0x1, 0x0); // Turn off LED at end of DMA routine |
||
1357 | lee | 502 | //ms_so_far= MIOS32_STOPWATCH_ValueGet(); // Check how long we've been in the routine up until this point |
1366 | lee | 503 | //DEBUG_MSG("%d.%d ms,%d.%d ms,%d.%d ms, %d voices",ms2_so_far/10,ms2_so_far%10,ms3_so_far/10,ms3_so_far%10,ms_so_far/10,ms_so_far%10,voice_no); |
1352 | lee | 504 | } |
505 | |||
506 | ///////////////////////////////////////////////////////////////////////////// |
||
507 | // initializes the synth |
||
508 | ///////////////////////////////////////////////////////////////////////////// |
||
509 | s32 SYNTH_Init(u32 mode) |
||
510 | { |
||
511 | // start I2S DMA transfers |
||
512 | return MIOS32_I2S_Start((u32 *)&sample_buffer[0], SAMPLE_BUFFER_SIZE, &SYNTH_ReloadSampleBuffer); |
||
513 | } |
||
514 | |||
515 | ///////////////////////////////////////////////////////////////////////////// |
||
1357 | lee | 516 | // This task is running endless in background |
517 | ///////////////////////////////////////////////////////////////////////////// |
||
518 | void APP_Background(void) |
||
519 | { |
||
520 | } |
||
521 | |||
522 | ///////////////////////////////////////////////////////////////////////////// |
||
1352 | lee | 523 | // This hook is called when an encoder has been moved |
524 | // incrementer is positive when encoder has been turned clockwise, else |
||
525 | // it is negative |
||
526 | ///////////////////////////////////////////////////////////////////////////// |
||
527 | void APP_ENC_NotifyChange(u32 encoder, s32 incrementer) |
||
528 | { |
||
529 | } |
||
530 | |||
531 | ///////////////////////////////////////////////////////////////////////////// |
||
532 | // This hook is called when a pot has been moved |
||
533 | ///////////////////////////////////////////////////////////////////////////// |
||
534 | void APP_AIN_NotifyChange(u32 pin, u32 pin_value) |
||
535 | { |
||
1353 | tk | 536 | } |
1366 | lee | 537 | |
538 | static void TASK_VOICE_SCAN(void *pvParameters) |
||
539 | { |
||
540 | u8 samp_no; |
||
1368 | lee | 541 | u8 new_voice_no; |
542 | |||
1366 | lee | 543 | portTickType xLastExecutionTime; |
544 | |||
545 | // Initialise the xLastExecutionTime variable on task entry |
||
546 | xLastExecutionTime = xTaskGetTickCount(); |
||
547 | |||
548 | while( 1 ) |
||
549 | { |
||
550 | vTaskDelayUntil(&xLastExecutionTime, 1 / portTICK_RATE_MS); // Run this every 1 ms, this WILL be interrupted every now and again by the DMA fill interrupt |
||
551 | |||
552 | // toggle Status LED to as a sign of live |
||
553 | //MIOS32_BOARD_LED_Set(1, ~MIOS32_BOARD_LED_Get()); |
||
554 | |||
1368 | lee | 555 | new_voice_no=0; |
1366 | lee | 556 | |
557 | // Work out which voices need to play which sample, this has lowest sample number priority |
||
558 | for(samp_no=0;samp_no<no_samples_loaded;samp_no++) |
||
559 | { |
||
1368 | lee | 560 | if(new_voice_no<POLYPHONY) // As long as not already at max voices, otherwise don't trigger/play |
1366 | lee | 561 | { |
562 | if(sample_on[samp_no]<0) // We want to play this voice (either newly triggered =1 or continue playing =2) |
||
563 | { |
||
1368 | lee | 564 | voice_samples[new_voice_no]=samp_no; // Assign the next available voice to this sample number |
565 | voice_velocity[new_voice_no]=(s16)(sample_vel[samp_no]); // Assign velocity to voice - cast required to ensure the voice accumulation multiply is fast signed 16 bit |
||
566 | new_voice_no++; // And increment number of voices in use |
||
1366 | lee | 567 | if(sample_on[samp_no]==-1) // Newly triggered sample (set to -1 by midi receive routine) |
568 | { |
||
569 | samplefile_pos[samp_no]=0; // Mark at position zero (used for sector reads and EOF calculations) |
||
570 | sample_on[samp_no]=-2; // Mark as on and don't retrigger on next loop |
||
571 | } |
||
572 | } |
||
573 | |||
574 | } |
||
575 | else |
||
576 | { break; } // stop looking if we're full! |
||
577 | } |
||
578 | |||
1368 | lee | 579 | if(!no_decay || new_voice_no<POLYPHONY) // Only process decaying notes if decay is enabled or we have any voices left |
1366 | lee | 580 | { |
581 | // now new notes allocated, fill up any remaining voices with decaying ones |
||
582 | for(samp_no=0;samp_no<no_samples_loaded;samp_no++) |
||
583 | { |
||
1368 | lee | 584 | if(new_voice_no<POLYPHONY) // As long as not already at max voices, otherwise don't trigger/play |
1366 | lee | 585 | { |
586 | if(sample_on[samp_no]>0) // positive number = decaying |
||
587 | { |
||
1368 | lee | 588 | voice_samples[new_voice_no]=samp_no; // Assign the next available voice to this sample number |
589 | voice_velocity[new_voice_no]=(s16)(sample_vel[samp_no]); |
||
590 | new_voice_no++; // And increment number of voices in use |
||
1366 | lee | 591 | sample_on[samp_no]--; // Decrement decay time |
592 | if(sample_on[samp_no]<0) { sample_vel[samp_no]=0; sample_on[samp_no]=0;} // If finished decaying mark as off |
||
593 | else |
||
594 | { |
||
595 | if((sample_on[samp_no]%8)==0) { |
||
596 | sample_vel[samp_no]-=sample_decay[samp_no]; // decrement volume by appropriate amount |
||
597 | //DEBUG_MSG("vel is %d, sample on is %d, sample_decay is %d",sample_vel[samp_no],sample_on[samp_no],sample_decay[samp_no]); |
||
598 | } |
||
599 | } |
||
600 | if(sample_vel[samp_no]<=0) { sample_vel[samp_no]=0; sample_on[samp_no]=0; } |
||
601 | } |
||
602 | |||
603 | } |
||
604 | else |
||
605 | { break;} // stop looking if we're full |
||
606 | } |
||
607 | } |
||
608 | |||
1368 | lee | 609 | voice_no=new_voice_no; // Set the global voice count now we're done |
1366 | lee | 610 | |
611 | } |
||
612 | } |
||
613 | |||
614 | static void TASK_BANKSWITCH_SCAN(void *pvParameters) |
||
615 | { |
||
616 | u8 this_bank; |
||
617 | portTickType xLastExecutionTime; |
||
618 | |||
619 | // Initialise the xLastExecutionTime variable on task entry |
||
620 | xLastExecutionTime = xTaskGetTickCount(); |
||
621 | |||
622 | while( 1 ) |
||
623 | { |
||
624 | vTaskDelayUntil(&xLastExecutionTime, 500 / portTICK_RATE_MS); // Run this every 0.5s, this WILL be interrupted every now and again by the DMA fill interrupt |
||
1371 | lee | 625 | if(lee_hw) // non standard bank switch connected and enabled in config file |
626 | { |
||
627 | // Now check for bank switch change |
||
628 | this_bank=Read_Switch(); // Get bank value |
||
629 | if(this_bank!=sample_bank_no) { |
||
630 | sample_bank_no=this_bank; // Set new bank |
||
631 | DEBUG_MSG("Changing bank to %d",sample_bank_no); |
||
632 | sdcard_access_allowed=0; |
||
633 | DEBUG_MSG("Opening new sample bank"); |
||
634 | Open_Bank(sample_bank_no); // Load relevant bank |
||
635 | sdcard_access_allowed=1; |
||
636 | } |
||
1366 | lee | 637 | } |
638 | } |
||
639 | } |