/[svn]/libgig/trunk/src/Akai.cpp
ViewVC logotype

Contents of /libgig/trunk/src/Akai.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3369 - (show annotations) (download)
Fri Nov 24 22:48:43 2017 UTC (6 years, 4 months ago) by schoenebeck
File size: 56600 byte(s)
* src/Akai.cpp: Fixed compilation error with recent, more
  strict compilers.
* Bumped version (4.0.0.svn34).

1 /*
2 libakai - C++ cross-platform akai sample disk reader
3 Copyright (C) 2002-2003 Sébastien Métrot
4
5 Linux port by Christian Schoenebeck <cuse@users.sourceforge.net> 2003-2017
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 */
21 // Akai.cpp
22
23 #include "Akai.h"
24
25 #include <stdio.h>
26 #include <string.h>
27 #include <unistd.h>
28 #include <fcntl.h>
29 #if defined(_CARBON_) || defined(__APPLE__)
30 # if defined (__GNUC__) && (__GNUC__ >= 4)
31 # include <sys/disk.h>
32 # else
33 # include <dev/disk.h>
34 # endif
35 #endif
36 #include <sys/types.h>
37 #include <sys/stat.h>
38 #include <errno.h>
39 #if defined(_CARBON_) || defined(__APPLE__)
40 #include <paths.h>
41 #include <sys/ioctl.h>
42 #include <sys/param.h>
43 #include <IOKit/IOKitLib.h>
44 #include <IOKit/IOBSD.h>
45 #include <IOKit/storage/IOMediaBSDClient.h>
46 #include <IOKit/storage/IOMedia.h>
47 #include <IOKit/storage/IOCDMedia.h>
48 #include <IOKit/storage/IOCDTypes.h>
49 #include <CoreFoundation/CoreFoundation.h>
50 #endif
51
52 #if defined(_CARBON_) || defined(__APPLE__)
53
54 // These definitions were taken from mount_cd9660.c
55 // There are some similar definitions in IOCDTypes.h
56 // however there seems to be some dissagreement in
57 // the definition of CDTOC.length
58 struct _CDMSF {
59 u_char minute;
60 u_char second;
61 u_char frame;
62 };
63
64 #define MSF_TO_LBA(msf) \
65 (((((msf).minute * 60UL) + (msf).second) * 75UL) + (msf).frame - 150)
66
67 struct _CDTOC_Desc {
68 u_char session;
69 u_char ctrl_adr; /* typed to be machine and compiler independent */
70 u_char tno;
71 u_char point;
72 struct _CDMSF address;
73 u_char zero;
74 struct _CDMSF p;
75 };
76
77 struct _CDTOC {
78 u_short length; /* in native cpu endian */
79 u_char first_session;
80 u_char last_session;
81 struct _CDTOC_Desc trackdesc[1];
82 };
83
84 // Most of the following Mac CDROM IO functions were taken from Apple's IOKit
85 // examples (BSD style license). ReadTOC() function was taken from the Bochs x86
86 // Emulator (LGPL). Most probably they have taken it however also from some
87 // other BSD style licensed example code as well ...
88
89 // Returns an iterator across all CD media (class IOCDMedia). Caller is responsible for releasing
90 // the iterator when iteration is complete.
91 static kern_return_t FindEjectableCDMedia(io_iterator_t *mediaIterator) {
92 kern_return_t kernResult;
93 CFMutableDictionaryRef classesToMatch;
94
95 // CD media are instances of class kIOCDMediaClass
96 classesToMatch = IOServiceMatching(kIOCDMediaClass);
97 if (classesToMatch == NULL) {
98 printf("IOServiceMatching returned a NULL dictionary.\n");
99 } else {
100 CFDictionarySetValue(classesToMatch, CFSTR(kIOMediaEjectableKey), kCFBooleanTrue);
101 // Each IOMedia object has a property with key kIOMediaEjectableKey which is true if the
102 // media is indeed ejectable. So add this property to the CFDictionary we're matching on.
103 }
104
105 kernResult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, mediaIterator);
106
107 return kernResult;
108 }
109
110 // Given an iterator across a set of CD media, return the BSD path to the
111 // next one. If no CD media was found the path name is set to an empty string.
112 static kern_return_t GetBSDPath(io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize) {
113 io_object_t nextMedia;
114 kern_return_t kernResult = KERN_FAILURE;
115
116 *bsdPath = '\0';
117
118 nextMedia = IOIteratorNext(mediaIterator);
119 if (nextMedia) {
120 CFTypeRef bsdPathAsCFString;
121
122 bsdPathAsCFString = IORegistryEntryCreateCFProperty(nextMedia,
123 CFSTR(kIOBSDNameKey),
124 kCFAllocatorDefault,
125 0);
126 if (bsdPathAsCFString) {
127 strlcpy(bsdPath, _PATH_DEV, maxPathSize);
128
129 // Add "r" before the BSD node name from the I/O Registry to specify the raw disk
130 // node. The raw disk nodes receive I/O requests directly and do not go through
131 // the buffer cache.
132
133 strlcat(bsdPath, "r", maxPathSize);
134
135 size_t devPathLength = strlen(bsdPath);
136
137 if (CFStringGetCString((CFStringRef)bsdPathAsCFString,
138 bsdPath + devPathLength,
139 maxPathSize - devPathLength,
140 kCFStringEncodingUTF8)) {
141 printf("BSD path: %s\n", bsdPath);
142 kernResult = KERN_SUCCESS;
143 }
144
145 CFRelease(bsdPathAsCFString);
146 }
147
148 IOObjectRelease(nextMedia);
149 }
150
151 return kernResult;
152 }
153
154 // Given the path to a CD drive, open the drive.
155 // Return the file descriptor associated with the device.
156 /*static int OpenDrive(const char *bsdPath) {
157 int fileDescriptor;
158
159 // This open() call will fail with a permissions error if the sample has been changed to
160 // look for non-removable media. This is because device nodes for fixed-media devices are
161 // owned by root instead of the current console user.
162
163 fileDescriptor = open(bsdPath, O_RDONLY);
164
165 if (fileDescriptor == -1) {
166 printf("Error opening device %s: ", bsdPath);
167 perror(NULL);
168 }
169
170 return fileDescriptor;
171 }*/
172
173 // Given the file descriptor for a whole-media CD device, read a sector from the drive.
174 // Return true if successful, otherwise false.
175 /*static Boolean ReadSector(int fileDescriptor) {
176 char *buffer;
177 ssize_t numBytes;
178 u_int32_t blockSize;
179
180 // This ioctl call retrieves the preferred block size for the media. It is functionally
181 // equivalent to getting the value of the whole media object's "Preferred Block Size"
182 // property from the IORegistry.
183 if (ioctl(fileDescriptor, DKIOCGETBLOCKSIZE, &blockSize)) {
184 perror("Error getting preferred block size");
185
186 // Set a reasonable default if we can't get the actual preferred block size. A real
187 // app would probably want to bail at this point.
188 blockSize = kCDSectorSizeCDDA;
189 }
190
191 printf("Media has block size of %d bytes.\n", blockSize);
192
193 // Allocate a buffer of the preferred block size. In a real application, performance
194 // can be improved by reading as many blocks at once as you can.
195 buffer = (char*) malloc(blockSize);
196
197 // Do the read. Note that we use read() here, not fread(), since this is a raw device
198 // node.
199 numBytes = read(fileDescriptor, buffer, blockSize);
200
201 // Free our buffer. Of course, a real app would do something useful with the data first.
202 free(buffer);
203
204 return numBytes == blockSize ? true : false;
205 }*/
206
207 // Given the file descriptor for a device, close that device.
208 /*static void CloseDrive(int fileDescriptor) {
209 close(fileDescriptor);
210 }*/
211
212 // path is the BSD path to a raw device such as /dev/rdisk1
213 static struct _CDTOC * ReadTOC(const char *devpath) {
214 struct _CDTOC * toc_p = NULL;
215 io_iterator_t iterator = 0;
216 io_registry_entry_t service = 0;
217 CFDictionaryRef properties = 0;
218 CFDataRef data = 0;
219 mach_port_t port = 0;
220 const char *devname;
221
222 if ((devname = strrchr(devpath, '/')) != NULL) {
223 ++devname;
224 } else {
225 devname = (char *) devpath;
226 }
227
228 if (IOMasterPort(bootstrap_port, &port) != KERN_SUCCESS) {
229 fprintf(stderr, "IOMasterPort failed\n");
230 goto Exit;
231 }
232
233 if (IOServiceGetMatchingServices(port, IOBSDNameMatching(port, 0, devname),
234 &iterator) != KERN_SUCCESS) {
235 fprintf(stderr, "IOServiceGetMatchingServices failed\n");
236 goto Exit;
237 }
238
239 service = IOIteratorNext(iterator);
240
241 IOObjectRelease(iterator);
242
243 iterator = 0;
244
245 while (service && !IOObjectConformsTo(service, "IOCDMedia")) {
246 if (IORegistryEntryGetParentIterator(service, kIOServicePlane,
247 &iterator) != KERN_SUCCESS)
248 {
249 fprintf(stderr, "IORegistryEntryGetParentIterator failed\n");
250 goto Exit;
251 }
252
253 IOObjectRelease(service);
254 service = IOIteratorNext(iterator);
255 IOObjectRelease(iterator);
256 }
257
258 if (!service) {
259 fprintf(stderr, "CD media not found\n");
260 goto Exit;
261 }
262
263 if (IORegistryEntryCreateCFProperties(service, (__CFDictionary **) &properties,
264 kCFAllocatorDefault,
265 kNilOptions) != KERN_SUCCESS)
266 {
267 fprintf(stderr, "IORegistryEntryGetParentIterator failed\n");
268 goto Exit;
269 }
270
271 data = (CFDataRef) CFDictionaryGetValue(properties, CFSTR(kIOCDMediaTOCKey));
272 if (data == NULL) {
273 fprintf(stderr, "CFDictionaryGetValue failed\n");
274 goto Exit;
275 } else {
276 CFRange range;
277 CFIndex buflen;
278
279 buflen = CFDataGetLength(data) + 1;
280 range = CFRangeMake(0, buflen);
281 toc_p = (struct _CDTOC *) malloc(buflen);
282 if (toc_p == NULL) {
283 fprintf(stderr, "Out of memory\n");
284 goto Exit;
285 } else {
286 CFDataGetBytes(data, range, (unsigned char *) toc_p);
287 }
288
289 /*
290 fprintf(stderr, "Table of contents\n length %d first %d last %d\n",
291 toc_p->length, toc_p->first_session, toc_p->last_session);
292 */
293
294 CFRelease(properties);
295 }
296
297 Exit:
298
299 if (service) {
300 IOObjectRelease(service);
301 }
302
303 return toc_p;
304 }
305
306 #endif // defined(_CARBON_) || defined(__APPLE__)
307
308 //////////////////////////////////
309 // AkaiSample:
310 AkaiSample::AkaiSample(DiskImage* pDisk, AkaiVolume* pParent, const AkaiDirEntry& DirEntry)
311 : AkaiDiskElement(pDisk->GetPos())
312 {
313 mpParent = pParent;
314 mpDisk = pDisk;
315 mDirEntry = DirEntry;
316 mpSamples = NULL;
317 mHeaderOK = false;
318 mPos = 0;
319
320 //
321 LoadHeader();
322 }
323
324 AkaiSample::~AkaiSample()
325 {
326 if (mpSamples)
327 free(mpSamples);
328 }
329
330 AkaiDirEntry AkaiSample::GetDirEntry()
331 {
332 return mDirEntry;
333 }
334
335 bool AkaiSample::LoadSampleData()
336 {
337 if (!LoadHeader())
338 return false;
339 if (mpSamples)
340 return true;
341
342 mpDisk->SetPos(mImageOffset);
343 mpSamples = (int16_t*) malloc(mNumberOfSamples * sizeof(int16_t));
344 if (!mpSamples)
345 return false;
346
347 mpDisk->ReadInt16((uint16_t*)mpSamples, mNumberOfSamples);
348 return true;
349 }
350
351 void AkaiSample::ReleaseSampleData()
352 {
353 if (!mpSamples)
354 return;
355 free(mpSamples);
356 mpSamples = NULL;
357 }
358
359 int AkaiSample::SetPos(int Where, akai_stream_whence_t Whence)
360 {
361 if (!mHeaderOK) return -1;
362 int w = Where;
363 switch (Whence)
364 {
365 case akai_stream_start:
366 mPos = w;
367 break;
368 /*case eStreamRewind:
369 w = -w;*/
370 case akai_stream_curpos:
371 mPos += w;
372 break;
373 case akai_stream_end:
374 mPos = mNumberOfSamples - w;
375 break;
376 }
377 if (mPos > mNumberOfSamples) mPos = mNumberOfSamples;
378 if (mPos < 0) mPos = 0;
379 return mPos;
380 }
381
382 int AkaiSample::Read(void* pBuffer, uint SampleCount)
383 {
384 if (!mHeaderOK) return 0;
385
386 if (mPos + SampleCount > mNumberOfSamples) SampleCount = mNumberOfSamples - mPos;
387
388 mpDisk->SetPos(mImageOffset + mPos * 2); // FIXME: assumes 16 bit sample depth
389 mpDisk->ReadInt16((uint16_t*)pBuffer, SampleCount);
390 return SampleCount;
391 }
392
393 bool AkaiSample::LoadHeader()
394 {
395 if (mHeaderOK)
396 return true;
397
398 mpDisk->SetPos(mpParent->GetParent()->GetOffset() + mDirEntry.mStart * AKAI_BLOCK_SIZE );
399
400 // Length Format Description
401 // --------------------------------------------------------------
402 // 1 3
403 if (mpDisk->ReadInt8() != AKAI_SAMPLE_ID)
404 return false;
405 // 1 Not important: 0 for 22050Hz, 1 for 44100Hz
406 mpDisk->ReadInt8();
407 // 1 unsigned MIDI root note (C3=60)
408 mMidiRootNote = mpDisk->ReadInt8();
409 // 12 AKAII Filename
410 char buffer[13];
411 mpDisk->Read(buffer, 12, 1);
412 AkaiToAscii(buffer, 12);
413 mName = buffer;
414
415 // 1 128
416 mpDisk->ReadInt8();
417 // 1 unsigned Number of active loops
418 mActiveLoops = mpDisk->ReadInt8();
419 // 1 unsigned char First active loop (0 for none)
420 mFirstActiveLoop = mpDisk->ReadInt8();
421 // 1 0
422 mpDisk->ReadInt8();
423 // 1 unsigned Loop mode: 0=in release 1=until release
424 // 2=none 3=play to end
425 mLoopMode = mpDisk->ReadInt8();
426 // 1 signed Cents tune -50...+50
427 mTuneCents = mpDisk->ReadInt8();
428 // 1 signed Semi tune -50...+50
429 mTuneSemitones = mpDisk->ReadInt8();
430 // 4 0,8,2,0
431 mpDisk->ReadInt8();
432 mpDisk->ReadInt8();
433 mpDisk->ReadInt8();
434 mpDisk->ReadInt8();
435 //
436 // 4 unsigned Number of sample words
437 mNumberOfSamples = mpDisk->ReadInt32();
438 // 4 unsigned Start marker
439 mStartMarker = mpDisk->ReadInt32();
440 // 4 unsigned End marker
441 mEndMarker = mpDisk->ReadInt32();
442 //
443 // 4 unsigned Loop 1 marker
444 // 2 unsigned Loop 1 fine length (65536ths)
445 // 4 unsigned Loop 1 coarse length (words)
446 // 2 unsigned Loop 1 time (msec. or 9999=infinite)
447 //
448 // 84 [as above] Loops 2 to 8
449 //
450 int i;
451 for (i=0; i<8; i++)
452 mLoops[i].Load(mpDisk);
453 // 4 0,0,255,255
454 mpDisk->ReadInt32();
455 // 2 unsigned Sampling frequency
456 mSamplingFrequency = mpDisk->ReadInt16();
457 // 1 signed char Loop tune offset -50...+50
458 mLoopTuneOffset = mpDisk->ReadInt8();
459
460 mImageOffset = mpParent->GetParent()->GetOffset() + mDirEntry.mStart * AKAI_BLOCK_SIZE + 150; // 150 is the size of the header
461
462 //FIXME: no header validation yet implemented
463 return (mHeaderOK = true);
464 }
465
466 bool AkaiSampleLoop::Load(DiskImage* pDisk)
467 {
468 // 4 unsigned Loop 1 marker
469 mMarker = pDisk->ReadInt32();
470 // 2 unsigned Loop 1 fine length (65536ths)
471 mFineLength = pDisk->ReadInt16();
472 // 4 unsigned Loop 1 coarse length (words)
473 mCoarseLength = pDisk->ReadInt32();
474 // 2 unsigned Loop 1 time (msec. or 9999=infinite)
475 mTime = pDisk->ReadInt16();
476 return true;
477 }
478
479 //////////////////////////////////
480 // AkaiProgram:
481 AkaiProgram::AkaiProgram(DiskImage* pDisk, AkaiVolume* pParent, const AkaiDirEntry& DirEntry)
482 : AkaiDiskElement(pDisk->GetPos())
483 {
484 mpParent = pParent;
485 mpDisk = pDisk;
486 mDirEntry = DirEntry;
487 mpKeygroups = NULL;
488 Load();
489 }
490
491 AkaiProgram::~AkaiProgram()
492 {
493 if (mpKeygroups)
494 delete[] mpKeygroups;
495 }
496
497 AkaiDirEntry AkaiProgram::GetDirEntry()
498 {
499 return mDirEntry;
500 }
501
502 bool AkaiProgram::Load()
503 {
504 // Read each of the program parameters
505 uint temppos = mpDisk->GetPos();
506 mpDisk->SetPos(mpParent->GetParent()->GetOffset() + mDirEntry.mStart * AKAI_BLOCK_SIZE );
507 // byte description default range/comments
508 // ---------------------------------------------------------------------------
509 // 1 program ID 1
510 uint8_t ProgID = mpDisk->ReadInt8();
511 if (ProgID != AKAI_PROGRAM_ID)
512 {
513 mpDisk->SetPos(temppos);
514 return false;
515 }
516 // 2-3 first keygroup address 150,0
517 /*uint16_t KeygroupAddress =*/ mpDisk->ReadInt16();
518 // 4-15 program name 10,10,10... AKAII character set
519 char buffer[13];
520 mpDisk->Read(buffer, 12, 1);
521 AkaiToAscii(buffer, 12);
522 mName = buffer;
523 // 16 MIDI program number 0 0..127
524 mMidiProgramNumber = mpDisk->ReadInt8();
525 // 17 MIDI channel 0 0..15, 255=OMNI
526 mMidiChannel = mpDisk->ReadInt8();
527 // 18 polyphony 15 1..16
528 mPolyphony = mpDisk->ReadInt8();
529 // 19 priority 1 0=LOW 1=NORM 2=HIGH 3=HOLD
530 mPriority = mpDisk->ReadInt8();
531 // 20 low key 24 24..127
532 mLowKey = mpDisk->ReadInt8();
533 // 21 high key 127 24..127
534 mHighKey = mpDisk->ReadInt8();
535 // 22 octave shift 0 -2..2
536 mOctaveShift = mpDisk->ReadInt8();
537 // 23 aux output select 255 0..7, 255=OFF
538 mAuxOutputSelect = mpDisk->ReadInt8();
539 // 24 mix output level 99 0..99
540 mMixOutputSelect = mpDisk->ReadInt8();
541 // 25 mix output pan 0 -50..50
542 mMixPan = mpDisk->ReadInt8();
543 // 26 volume 80 0..99
544 mVolume = mpDisk->ReadInt8();
545 // 27 vel>volume 20 -50..50
546 mVelocityToVolume = mpDisk->ReadInt8();
547 // 28 key>volume 0 -50..50
548 mKeyToVolume = mpDisk->ReadInt8();
549 // 29 pres>volume 0 -50..50
550 mPressureToVolume = mpDisk->ReadInt8();
551 // 30 pan lfo rate 50 0..99
552 mPanLFORate = mpDisk->ReadInt8();
553 // 31 pan lfo depth 0 0..99
554 mPanLFODepth = mpDisk->ReadInt8();
555 // 32 pan lfo delay 0 0..99
556 mPanLFODelay = mpDisk->ReadInt8();
557 // 33 key>pan 0 -50..50
558 mKeyToPan = mpDisk->ReadInt8();
559 // 34 lfo rate 50 0..99
560 mLFORate = mpDisk->ReadInt8();
561 // 35 lfo depth 0 0..99
562 mLFODepth = mpDisk->ReadInt8();
563 // 36 lfo delay 0 0..99
564 mLFODelay = mpDisk->ReadInt8();
565 // 37 mod>lfo depth 30 0..99
566 mModulationToLFODepth = mpDisk->ReadInt8();
567 // 38 pres>lfo depth 0 0..99
568 mPressureToLFODepth = mpDisk->ReadInt8();
569 // 39 vel>lfo depth 0 0..99
570 mVelocityToLFODepth = mpDisk->ReadInt8();
571 // 40 bend>pitch 2 0..12 semitones
572 mBendToPitch = mpDisk->ReadInt8();
573 // 41 pres>pitch 0 -12..12 semitones
574 mPressureToPitch = mpDisk->ReadInt8();
575 // 42 keygroup crossfade 0 0=OFF 1=ON
576 mKeygroupCrossfade = mpDisk->ReadInt8()?true:false;
577 // 43 number of keygroups 1 1..99
578 mNumberOfKeygroups = mpDisk->ReadInt8();
579 // 44 (internal use) 0 program number
580 mpDisk->ReadInt8();
581 // 45-56 key temperament C,C#,D... 0 -25..25 cents
582 uint i;
583 for (i = 0; i<11; i++)
584 mKeyTemperament[i] = mpDisk->ReadInt8();
585 // 57 fx output 0 0=OFF 1=ON
586 mFXOutput = mpDisk->ReadInt8()?true:false;
587 // 58 mod>pan 0 -50..50
588 mModulationToPan = mpDisk->ReadInt8();
589 // 59 stereo coherence 0 0=OFF 1=ON
590 mStereoCoherence = mpDisk->ReadInt8()?true:false;
591 // 60 lfo desync 1 0=OFF 1=ON
592 mLFODesync = mpDisk->ReadInt8()?true:false;
593 // 61 pitch law 0 0=LINEAR
594 mPitchLaw = mpDisk->ReadInt8();
595 // 62 voice re-assign 0 0=OLDEST 1=QUIETEST
596 mVoiceReassign = mpDisk->ReadInt8();
597 // 63 softped>volume 10 0..99
598 mSoftpedToVolume = mpDisk->ReadInt8();
599 // 64 softped>attack 10 0..99
600 mSoftpedToAttack = mpDisk->ReadInt8();
601 // 65 softped>filt 10 0..99
602 mSoftpedToFilter = mpDisk->ReadInt8();
603 // 66 tune cents 0 -128..127 (-50..50 cents)
604 mSoftpedToTuneCents = mpDisk->ReadInt8();
605 // 67 tune semitones 0 -50..50
606 mSoftpedToTuneSemitones = mpDisk->ReadInt8();
607 // 68 key>lfo rate 0 -50..50
608 mKeyToLFORate = mpDisk->ReadInt8();
609 // 69 key>lfo depth 0 -50..50
610 mKeyToLFODepth = mpDisk->ReadInt8();
611 // 70 key>lfo delay 0 -50..50
612 mKeyToLFODelay = mpDisk->ReadInt8();
613 // 71 voice output scale 1 0=-6dB 1=0dB 2=+12dB
614 mVoiceOutputScale = mpDisk->ReadInt8();
615 // 72 stereo output scale 0 0=0dB 1=+6dB
616 mStereoOutputScale = mpDisk->ReadInt8();
617 // 73-150 (not used)
618
619 // Read the key groups:
620 if (mpKeygroups)
621 delete[] mpKeygroups;
622 mpKeygroups = new AkaiKeygroup[mNumberOfKeygroups];
623 for (i = 0; i < mNumberOfKeygroups; i++)
624 {
625 // Go where the key group is on the disk:
626 mpDisk->SetPos(mpParent->GetParent()->GetOffset() + mDirEntry.mStart * AKAI_BLOCK_SIZE + 150 * (i+1));
627 if (!mpKeygroups[i].Load(mpDisk))
628 {
629 mpDisk->SetPos(temppos);
630 return false;
631 }
632 }
633
634 mpDisk->SetPos(temppos);
635 return true;
636 }
637
638 uint AkaiProgram::ListSamples(std::list<String>& rSamples)
639 {
640 return 0;
641 }
642
643 AkaiSample* AkaiProgram::GetSample(uint Index)
644 {
645 return NULL;
646 }
647
648 AkaiSample* AkaiProgram::GetSample(const String& rName)
649 {
650 return NULL;
651 }
652
653 // AkaiKeygroup:
654 bool AkaiKeygroup::Load(DiskImage* pDisk)
655 {
656 uint i;
657 // byte description default range/comments
658 // ---------------------------------------------------------------------------
659 // 1 keygroup ID 2
660 if (pDisk->ReadInt8() != AKAI_KEYGROUP_ID)
661 return false;
662 // 2-3 next keygroup address 44,1 300,450,600,750.. (16-bit)
663 /*uint16_t NextKeygroupAddress =*/ pDisk->ReadInt16();
664 // 4 low key 24 24..127
665 mLowKey = pDisk->ReadInt8();
666 // 5 high key 127 24..127
667 mHighKey = pDisk->ReadInt8();
668 // 6 tune cents 0 -128..127 (-50..50 cents)
669 mTuneCents = pDisk->ReadInt8();
670 // 7 tune semitones 0 -50..50
671 mTuneSemitones = pDisk->ReadInt8();
672 // 8 filter 99 0..99
673 mFilter = pDisk->ReadInt8();
674 // 9 key>filter 12 0..24 semitone/oct
675 mKeyToFilter = pDisk->ReadInt8();
676 // 10 vel>filt 0 -50..50
677 mVelocityToFilter = pDisk->ReadInt8();
678 // 11 pres>filt 0 -50..50
679 mPressureToFilter = pDisk->ReadInt8();
680 // 12 env2>filt 0 -50..50
681 mEnveloppe2ToFilter = pDisk->ReadInt8();
682
683 // 13 env1 attack 0 0..99
684 // 14 env1 decay 30 0..99
685 // 15 env1 sustain 99 0..99
686 // 16 env1 release 45 0..99
687 // 17 env1 vel>attack 0 -50..50
688 // 18 env1 vel>release 0 -50..50
689 // 19 env1 offvel>release 0 -50..50
690 // 20 env1 key>dec&rel 0 -50..50
691 // 21 env2 attack 0 0..99
692 // 22 env2 decay 50 0..99
693 // 23 env2 sustain 99 0..99
694 // 24 env2 release 45 0..99
695 // 25 env2 vel>attack 0 -50..50
696 // 26 env2 vel>release 0 -50..50
697 // 27 env2 offvel>release 0 -50..50
698 // 28 env2 key>dec&rel 0 -50..50
699 for (i=0; i<2; i++)
700 mEnveloppes[i].Load(pDisk);
701
702 // 29 vel>env2>filter 0 -50..50
703 mVelocityToEnveloppe2ToFilter = pDisk->ReadInt8();
704 // 30 env2>pitch 0 -50..50
705 mEnveloppe2ToPitch = pDisk->ReadInt8();
706 // 31 vel zone crossfade 1 0=OFF 1=ON
707 mVelocityZoneCrossfade = pDisk->ReadInt8()?true:false;
708 // 32 vel zones used 4
709 mVelocityZoneUsed = pDisk->ReadInt8();
710 // 33 (internal use) 255
711 pDisk->ReadInt8();
712 // 34 (internal use) 255
713 pDisk->ReadInt8();
714 //
715
716 // 35-46 sample 1 name 10,10,10... AKAII character set
717 // 47 low vel 0 0..127
718 // 48 high vel 127 0..127
719 // 49 tune cents 0 -128..127 (-50..50 cents)
720 // 50 tune semitones 0 -50..50
721 // 51 loudness 0 -50..+50
722 // 52 filter 0 -50..+50
723 // 53 pan 0 -50..+50
724 // 54 loop mode 0 0=AS_SAMPLE 1=LOOP_IN_REL
725 // 2=LOOP_UNTIL_REL 3=NO_LOOP
726 // 4=PLAY_TO_END
727 // 55 (internal use) 255
728 // 56 (internal use) 255
729 // 57-58 (internal use) 44,1
730 //
731 // 59-82 [repeat 35-58 for sample 2]
732 //
733 // 83-106 [repeat 35-58 for sample 3]
734 //
735 // 107-130 [repeat 35-58 for sample 4]
736 //
737 for (i=0; i<4; i++)
738 mSamples[i].Load(pDisk);
739
740 // 131 beat detune 0 -50..50
741 mBeatDetune = pDisk->ReadInt8();
742 // 132 hold attack until loop 0 0=OFF 1=ON
743 mHoldAttackUntilLoop = pDisk->ReadInt8()?true:false;
744 // 133-136 sample 1-4 key tracking 0 0=TRACK 1=FIXED
745 for (i=0; i<4; i++)
746 mSampleKeyTracking[i] = pDisk->ReadInt8()?true:false;
747 // 137-140 sample 1-4 aux out offset 0 0..7
748 for (i=0; i<4; i++)
749 mSampleAuxOutOffset[i] = pDisk->ReadInt8();
750 // 141-148 vel>sample start 0 -9999..9999 (16-bit signed)
751 for (i=0; i<4; i++)
752 mVelocityToSampleStart[i] = pDisk->ReadInt8();
753 // 149 vel>volume offset 0 -50..50
754 for (i=0; i<4; i++)
755 mVelocityToVolumeOffset[i] = pDisk->ReadInt8();
756 // 150 (not used)
757
758 return true;
759 }
760
761 bool AkaiEnveloppe::Load(DiskImage* pDisk)
762 {
763 // 13 env1 attack 0 0..99
764 mAttack = pDisk->ReadInt8();
765 // 14 env1 decay 30 0..99
766 mDecay = pDisk->ReadInt8();
767 // 15 env1 sustain 99 0..99
768 mSustain = pDisk->ReadInt8();
769 // 16 env1 release 45 0..99
770 mRelease = pDisk->ReadInt8();
771 // 17 env1 vel>attack 0 -50..50
772 mVelocityToAttack = pDisk->ReadInt8();
773 // 18 env1 vel>release 0 -50..50
774 mVelocityToRelease = pDisk->ReadInt8();
775 // 19 env1 offvel>release 0 -50..50
776 mOffVelocityToRelease = pDisk->ReadInt8();
777 // 20 env1 key>dec&rel 0 -50..50
778 mKeyToDecayAndRelease = pDisk->ReadInt8();
779 return true;
780 }
781
782 bool AkaiKeygroupSample::Load(DiskImage* pDisk)
783 {
784 // 35-46 sample 1 name 10,10,10... AKAII character set
785 char buffer[13];
786 pDisk->Read(buffer, 12, 1);
787 AkaiToAscii(buffer, 12);
788 mName = buffer;
789
790 // 47 low vel 0 0..127
791 mLowLevel = pDisk->ReadInt8();
792 // 48 high vel 127 0..127
793 /*uint8_t mHighLevel =*/ pDisk->ReadInt8();
794 // 49 tune cents 0 -128..127 (-50..50 cents)
795 /*int8_t mTuneCents =*/ pDisk->ReadInt8();
796 // 50 tune semitones 0 -50..50
797 /*int8_t mTuneSemitones =*/ pDisk->ReadInt8();
798 // 51 loudness 0 -50..+50
799 /*int8_t mLoudness =*/ pDisk->ReadInt8();
800 // 52 filter 0 -50..+50
801 /*int8_t mFilter =*/ pDisk->ReadInt8();
802 // 53 pan 0 -50..+50
803 /*int8_t mPan =*/ pDisk->ReadInt8();
804 // 54 loop mode 0 0=AS_SAMPLE 1=LOOP_IN_REL
805 // 2=LOOP_UNTIL_REL 3=NO_LOOP
806 // 4=PLAY_TO_END
807 /*uint8_t mLoopMode =*/ pDisk->ReadInt8();
808 // 55 (internal use) 255
809 pDisk->ReadInt8();
810 // 56 (internal use) 255
811 pDisk->ReadInt8();
812 // 57-58 (internal use) 44,1
813 pDisk->ReadInt16();
814 //
815
816 return true;
817 }
818
819 //////////////////////////////////
820 // AkaiVolume:
821 AkaiVolume::AkaiVolume(DiskImage* pDisk, AkaiPartition* pParent, const AkaiDirEntry& DirEntry)
822 : AkaiDiskElement()
823 {
824 mpDisk = pDisk;
825 mpParent = pParent;
826 mDirEntry = DirEntry;
827
828 if (mDirEntry.mType != AKAI_TYPE_DIR_S1000 && mDirEntry.mType != AKAI_TYPE_DIR_S3000)
829 {
830 printf("Creating Unknown Volume type! %d\n",mDirEntry.mType);
831 #ifdef WIN32
832 OutputDebugString("Creating Unknown Volume type!\n");
833 #endif
834 }
835 }
836
837 AkaiVolume::~AkaiVolume()
838 {
839 {
840 std::list<AkaiProgram*>::iterator it;
841 std::list<AkaiProgram*>::iterator end = mpPrograms.end();
842 for (it = mpPrograms.begin(); it != end; it++)
843 if (*it)
844 (*it)->Release();
845 }
846
847 {
848 std::list<AkaiSample*>::iterator it;
849 std::list<AkaiSample*>::iterator end = mpSamples.end();
850 for (it = mpSamples.begin(); it != end; it++)
851 if (*it)
852 (*it)->Release();
853 }
854 }
855
856 uint AkaiVolume::ReadDir()
857 {
858 uint i;
859 if (mpPrograms.empty())
860 {
861 uint maxfiles = ReadFAT(mpDisk, mpParent,mDirEntry.mStart) ? AKAI_MAX_FILE_ENTRIES_S1000 : AKAI_MAX_FILE_ENTRIES_S3000;
862 for (i = 0; i < maxfiles; i++)
863 {
864 AkaiDirEntry DirEntry;
865 ReadDirEntry(mpDisk, mpParent, DirEntry, mDirEntry.mStart, i);
866 DirEntry.mIndex = i;
867 if (DirEntry.mType == 'p')
868 {
869 AkaiProgram* pProgram = new AkaiProgram(mpDisk, this, DirEntry);
870 pProgram->Acquire();
871 mpPrograms.push_back(pProgram);
872 }
873 else if (DirEntry.mType == 's')
874 {
875 AkaiSample* pSample = new AkaiSample(mpDisk, this, DirEntry);
876 pSample->Acquire();
877 mpSamples.push_back(pSample);
878 }
879 }
880 }
881 return (uint)(mpPrograms.size() + mpSamples.size());
882 }
883
884 uint AkaiVolume::ListPrograms(std::list<AkaiDirEntry>& rPrograms)
885 {
886 rPrograms.clear();
887 ReadDir();
888
889 std::list<AkaiProgram*>::iterator it;
890 std::list<AkaiProgram*>::iterator end = mpPrograms.end();
891 for (it = mpPrograms.begin(); it != end; it++)
892 if (*it)
893 rPrograms.push_back((*it)->GetDirEntry());
894 return (uint)rPrograms.size();
895 }
896
897 AkaiProgram* AkaiVolume::GetProgram(uint Index)
898 {
899 uint i = 0;
900
901 if (mpPrograms.empty())
902 {
903 std::list<AkaiDirEntry> dummy;
904 ListPrograms(dummy);
905 }
906
907 std::list<AkaiProgram*>::iterator it;
908 std::list<AkaiProgram*>::iterator end = mpPrograms.end();
909 for (it = mpPrograms.begin(); it != end; it++)
910 {
911 if (*it && i == Index)
912 {
913 (*it)->Acquire();
914 return *it;
915 }
916 i++;
917 }
918 return NULL;
919 }
920
921 AkaiProgram* AkaiVolume::GetProgram(const String& rName)
922 {
923 if (mpPrograms.empty())
924 {
925 std::list<AkaiDirEntry> dummy;
926 ListPrograms(dummy);
927 }
928
929 std::list<AkaiProgram*>::iterator it;
930 std::list<AkaiProgram*>::iterator end = mpPrograms.end();
931 for (it = mpPrograms.begin(); it != end; it++)
932 {
933 if (*it && rName == (*it)->GetDirEntry().mName)
934 {
935 (*it)->Acquire();
936 return *it;
937 }
938 }
939 return NULL;
940 }
941
942 uint AkaiVolume::ListSamples(std::list<AkaiDirEntry>& rSamples)
943 {
944 rSamples.clear();
945 ReadDir();
946
947 std::list<AkaiSample*>::iterator it;
948 std::list<AkaiSample*>::iterator end = mpSamples.end();
949 for (it = mpSamples.begin(); it != end; it++)
950 if (*it)
951 rSamples.push_back((*it)->GetDirEntry());
952 return (uint)rSamples.size();
953 }
954
955 AkaiSample* AkaiVolume::GetSample(uint Index)
956 {
957 uint i = 0;
958
959 if (mpSamples.empty())
960 {
961 std::list<AkaiDirEntry> dummy;
962 ListSamples(dummy);
963 }
964
965 std::list<AkaiSample*>::iterator it;
966 std::list<AkaiSample*>::iterator end = mpSamples.end();
967 for (it = mpSamples.begin(); it != end; it++)
968 {
969 if (*it && i == Index)
970 {
971 (*it)->Acquire();
972 return *it;
973 }
974 i++;
975 }
976 return NULL;
977 }
978
979 AkaiSample* AkaiVolume::GetSample(const String& rName)
980 {
981 if (mpSamples.empty())
982 {
983 std::list<AkaiDirEntry> dummy;
984 ListSamples(dummy);
985 }
986
987 std::list<AkaiSample*>::iterator it;
988 std::list<AkaiSample*>::iterator end = mpSamples.end();
989 for (it = mpSamples.begin(); it != end; it++)
990 {
991 if (*it && rName == (*it)->GetDirEntry().mName)
992 {
993 (*it)->Acquire();
994 return *it;
995 }
996 }
997 return NULL;
998 }
999
1000 AkaiDirEntry AkaiVolume::GetDirEntry()
1001 {
1002 return mDirEntry;
1003 }
1004
1005 bool AkaiVolume::IsEmpty()
1006 {
1007 return ReadDir() == 0;
1008 }
1009
1010
1011 //////////////////////////////////
1012 // AkaiPartition:
1013 AkaiPartition::AkaiPartition(DiskImage* pDisk, AkaiDisk* pParent)
1014 {
1015 mpDisk = pDisk;
1016 mpParent = pParent;
1017 }
1018
1019 AkaiPartition::~AkaiPartition()
1020 {
1021 std::list<AkaiVolume*>::iterator it;
1022 std::list<AkaiVolume*>::iterator end = mpVolumes.end();
1023 for (it = mpVolumes.begin(); it != end; it++)
1024 if (*it)
1025 (*it)->Release();
1026 }
1027
1028 uint AkaiPartition::ListVolumes(std::list<AkaiDirEntry>& rVolumes)
1029 {
1030 rVolumes.clear();
1031 uint i;
1032 if (mpVolumes.empty())
1033 {
1034 for (i = 0; i < AKAI_MAX_DIR_ENTRIES; i++)
1035 {
1036 AkaiDirEntry DirEntry;
1037 ReadDirEntry(mpDisk, this, DirEntry, AKAI_ROOT_ENTRY_OFFSET, i);
1038 DirEntry.mIndex = i;
1039 if (DirEntry.mType == AKAI_TYPE_DIR_S1000 || DirEntry.mType == AKAI_TYPE_DIR_S3000)
1040 {
1041 AkaiVolume* pVolume = new AkaiVolume(mpDisk, this, DirEntry);
1042 pVolume->Acquire();
1043 if (!pVolume->IsEmpty())
1044 {
1045 mpVolumes.push_back(pVolume);
1046 rVolumes.push_back(DirEntry);
1047 }
1048 else
1049 pVolume->Release();
1050 }
1051 }
1052 }
1053 else
1054 {
1055 std::list<AkaiVolume*>::iterator it;
1056 std::list<AkaiVolume*>::iterator end = mpVolumes.end();
1057 for (it = mpVolumes.begin(); it != end; it++)
1058 if (*it)
1059 rVolumes.push_back((*it)->GetDirEntry());
1060 }
1061 return (uint)rVolumes.size();
1062 }
1063
1064 AkaiVolume* AkaiPartition::GetVolume(uint Index)
1065 {
1066 uint i = 0;
1067
1068 if (mpVolumes.empty())
1069 {
1070 std::list<AkaiDirEntry> dummy;
1071 ListVolumes(dummy);
1072 }
1073
1074 std::list<AkaiVolume*>::iterator it;
1075 std::list<AkaiVolume*>::iterator end = mpVolumes.end();
1076 for (it = mpVolumes.begin(); it != end; it++)
1077 {
1078 if (*it && i == Index)
1079 {
1080 (*it)->Acquire();
1081 return *it;
1082 }
1083 i++;
1084 }
1085 return NULL;
1086 }
1087
1088 AkaiVolume* AkaiPartition::GetVolume(const String& rName)
1089 {
1090 if (mpVolumes.empty())
1091 {
1092 std::list<AkaiDirEntry> dummy;
1093 ListVolumes(dummy);
1094 }
1095
1096 std::list<AkaiVolume*>::iterator it;
1097 std::list<AkaiVolume*>::iterator end = mpVolumes.end();
1098 for (it = mpVolumes.begin(); it != end; it++)
1099 {
1100 if (*it && rName == (*it)->GetDirEntry().mName)
1101 {
1102 (*it)->Acquire();
1103 return *it;
1104 }
1105 }
1106 return NULL;
1107 }
1108
1109 bool AkaiPartition::IsEmpty()
1110 {
1111 std::list<AkaiDirEntry> Volumes;
1112 return ListVolumes(Volumes) == 0;
1113 }
1114
1115
1116 //////////////////////////////////
1117 // AkaiDisk:
1118 AkaiDisk::AkaiDisk(DiskImage* pDisk)
1119 {
1120 mpDisk = pDisk;
1121 }
1122
1123 AkaiDisk::~AkaiDisk()
1124 {
1125 std::list<AkaiPartition*>::iterator it;
1126 std::list<AkaiPartition*>::iterator end = mpPartitions.end();
1127 for (it = mpPartitions.begin(); it != end ; it++)
1128 if (*it)
1129 (*it)->Release();
1130 }
1131
1132 uint AkaiDisk::GetPartitionCount()
1133 {
1134 if (!mpPartitions.empty())
1135 return (uint)mpPartitions.size();
1136
1137 uint offset = 0;
1138 uint16_t size = 0;
1139 while (size != AKAI_PARTITION_END_MARK && size != 0x0fff && size != 0xffff
1140 && size<30720 && mpPartitions.size()<9)
1141 {
1142 // printf("size: %x\t",size);
1143 AkaiPartition* pPartition = new AkaiPartition(mpDisk,this);
1144 pPartition->Acquire();
1145 pPartition->SetOffset(offset);
1146
1147 if (!pPartition->IsEmpty())
1148 {
1149 mpPartitions.push_back(pPartition); // Add this partitions' offset to the list.
1150 }
1151
1152 mpDisk->SetPos(offset);
1153 if (!mpDisk->ReadInt16(&size))
1154 return (uint)mpPartitions.size();
1155 uint t = size;
1156 offset += AKAI_BLOCK_SIZE * t;
1157 // printf("new offset %d / size %d\n",offset,size);
1158 }
1159
1160 return (uint)mpPartitions.size();
1161 }
1162
1163 AkaiPartition* AkaiDisk::GetPartition(uint count)
1164 {
1165 std::list<AkaiPartition*>::iterator it;
1166 std::list<AkaiPartition*>::iterator end = mpPartitions.end();
1167 uint i = 0;
1168 for (i = 0, it = mpPartitions.begin(); it != end && i != count; i++) it++;
1169
1170 if (i != count || it == end)
1171 return NULL;
1172
1173 (*it)->Acquire();
1174 return *it;
1175 }
1176
1177 /////////////////////////
1178 // AkaiDiskElement
1179
1180 int AkaiDiskElement::ReadFAT(DiskImage* pDisk, AkaiPartition* pPartition, int block)
1181 {
1182 int16_t value = 0;
1183 pDisk->SetPos(pPartition->GetOffset()+AKAI_FAT_OFFSET + block*2);
1184 pDisk->Read(&value, 2,1);
1185 return value;
1186 }
1187
1188
1189 bool AkaiDiskElement::ReadDirEntry(DiskImage* pDisk, AkaiPartition* pPartition, AkaiDirEntry& rEntry, int block, int pos)
1190 {
1191 char buffer[13];
1192
1193 if (block == AKAI_ROOT_ENTRY_OFFSET)
1194 {
1195 pDisk->SetPos(pPartition->GetOffset()+AKAI_DIR_ENTRY_OFFSET + pos * AKAI_DIR_ENTRY_SIZE);
1196 pDisk->Read(buffer, 12, 1);
1197 AkaiToAscii(buffer, 12);
1198 rEntry.mName = buffer;
1199
1200 pDisk->ReadInt16(&rEntry.mType);
1201 pDisk->ReadInt16(&rEntry.mStart);
1202 rEntry.mSize = 0;
1203 return true;
1204 }
1205 else
1206 {
1207 if (pos < 341)
1208 {
1209 pDisk->SetPos(block * AKAI_BLOCK_SIZE + pos * AKAI_FILE_ENTRY_SIZE + pPartition->GetOffset());
1210 }
1211 else
1212 {
1213 int temp;
1214 temp = ReadFAT(pDisk, pPartition, block);
1215 pDisk->SetPos(pPartition->GetOffset()+temp * AKAI_BLOCK_SIZE + (pos - 341) * AKAI_FILE_ENTRY_SIZE);
1216 }
1217
1218 pDisk->Read(buffer, 12, 1);
1219 AkaiToAscii(buffer, 12);
1220 rEntry.mName = buffer;
1221
1222 uint8_t t1,t2,t3;
1223 pDisk->SetPos(4,akai_stream_curpos);
1224 pDisk->Read(&t1, 1,1);
1225 rEntry.mType = t1;
1226
1227 pDisk->Read(&t1, 1,1);
1228 pDisk->Read(&t2, 1,1);
1229 pDisk->Read(&t3, 1,1);
1230 rEntry.mSize = (unsigned char)t1 | ((unsigned char)t2 <<8) | ((unsigned char)t3<<16);
1231
1232 pDisk->ReadInt16(&rEntry.mStart,1);
1233 return true;
1234 } // not root block
1235 }
1236
1237 void AkaiDiskElement::AkaiToAscii(char * buffer, int length)
1238 {
1239 int i;
1240
1241 for (i = 0; i < length; i++)
1242 {
1243 if (buffer[i]>=0 && buffer[i]<=9)
1244 buffer[i] +=48;
1245 else if (buffer[i]==10)
1246 buffer[i] = 32;
1247 else if (buffer[i]>=11 && buffer[i]<=36)
1248 buffer[i] = 64+(buffer[i]-10);
1249 else
1250 buffer[i] = 32;
1251 }
1252 buffer[length] = '\0';
1253 while (length-- > 0 && buffer[length] == 32)
1254 {
1255 // This block intentionaly left blank :)
1256 }
1257 buffer[length+1] = '\0';
1258 }
1259
1260
1261 //////////////////////////////////
1262 // DiskImage:
1263
1264 DiskImage::DiskImage(const char* path)
1265 {
1266 Init();
1267 OpenStream(path);
1268 }
1269
1270 #ifdef _CARBON_
1271 kern_return_t FindEjectableCDMedia( io_iterator_t *mediaIterator )
1272 {
1273 kern_return_t kernResult;
1274 mach_port_t masterPort;
1275 CFMutableDictionaryRef classesToMatch;
1276
1277 kernResult = IOMasterPort( MACH_PORT_NULL, &masterPort );
1278 if ( KERN_SUCCESS != kernResult )
1279 {
1280 printf( "IOMasterPort returned %d\n", kernResult );
1281 }
1282
1283 classesToMatch = IOServiceMatching( kIOCDMediaClass );
1284 if ( classesToMatch == NULL )
1285 {
1286 printf( "IOServiceMatching returned a NULL dictionary.\n" );
1287 }
1288 else
1289 {
1290 CFDictionarySetValue( classesToMatch, CFSTR( kIOMediaEjectableKey ), kCFBooleanTrue );
1291 }
1292
1293 kernResult = IOServiceGetMatchingServices( masterPort, classesToMatch, mediaIterator );
1294 if ( KERN_SUCCESS != kernResult )
1295 {
1296 printf( "IOServiceGetMatchingServices returned %d\n", kernResult );
1297 }
1298
1299 return kernResult;
1300 }
1301
1302 kern_return_t GetBSDPath( io_iterator_t mediaIterator, char *bsdPath, CFIndex maxPathSize )
1303 {
1304 io_object_t nextMedia;
1305 kern_return_t kernResult = KERN_FAILURE;
1306
1307 *bsdPath = '\0';
1308
1309 nextMedia = IOIteratorNext( mediaIterator );
1310 if ( nextMedia )
1311 {
1312 CFTypeRef bsdPathAsCFString;
1313
1314 bsdPathAsCFString = IORegistryEntryCreateCFProperty( nextMedia,
1315 CFSTR( kIOBSDNameKey ),
1316 kCFAllocatorDefault,
1317 0 );
1318 if ( bsdPathAsCFString )
1319 {
1320 size_t devPathLength;
1321
1322 strcpy( bsdPath, _PATH_DEV );
1323 strcat( bsdPath, "r" );
1324
1325 devPathLength = strlen( bsdPath );
1326
1327 if ( CFStringGetCString( (__CFString*)bsdPathAsCFString,
1328 bsdPath + devPathLength,
1329 maxPathSize - devPathLength,
1330 kCFStringEncodingASCII ) )
1331 {
1332 printf( "BSD path: %s\n", bsdPath );
1333 kernResult = KERN_SUCCESS;
1334 }
1335
1336 CFRelease( bsdPathAsCFString );
1337 }
1338 IOObjectRelease( nextMedia );
1339 }
1340
1341 return kernResult;
1342 }
1343
1344 struct _CDMSF
1345 {
1346 u_char minute;
1347 u_char second;
1348 u_char frame;
1349 };
1350
1351 /* converting from minute-second to logical block entity */
1352 #define MSF_TO_LBA(msf) (((((msf).minute * 60UL) + (msf).second) * 75UL) + (msf).frame - 150)
1353
1354 struct _CDTOC_Desc
1355 {
1356 u_char session;
1357 u_char ctrl_adr; /* typed to be machine and compiler independent */
1358 u_char tno;
1359 u_char point;
1360 struct _CDMSF address;
1361 u_char zero;
1362 struct _CDMSF p;
1363 };
1364
1365 struct _CDTOC
1366 {
1367 u_short length; /* in native cpu endian */
1368 u_char first_session;
1369 u_char last_session;
1370 struct _CDTOC_Desc trackdesc[1];
1371 };
1372
1373 static struct _CDTOC * ReadTOC (const char * devpath )
1374 {
1375 struct _CDTOC * toc_p = NULL;
1376 io_iterator_t iterator = 0;
1377 io_registry_entry_t service = 0;
1378 CFDictionaryRef properties = 0;
1379 CFDataRef data = 0;
1380 mach_port_t port = 0;
1381 char * devname;
1382 if (( devname = strrchr( devpath, '/' )) != NULL )
1383 {
1384 ++devname;
1385 }
1386 else
1387 {
1388 devname = ( char *) devpath;
1389 }
1390
1391 if ( IOMasterPort(bootstrap_port, &port ) != KERN_SUCCESS )
1392 {
1393 printf( "IOMasterPort failed\n" ); goto Exit ;
1394 }
1395 if ( IOServiceGetMatchingServices( port, IOBSDNameMatching( port, 0, devname ),
1396 &iterator ) != KERN_SUCCESS )
1397 {
1398 printf( "IOServiceGetMatchingServices failed\n" ); goto Exit ;
1399 }
1400
1401 char buffer[1024];
1402 IOObjectGetClass(iterator,buffer);
1403 printf("Service: %s\n",buffer);
1404
1405
1406 IOIteratorReset (iterator);
1407 service = IOIteratorNext( iterator );
1408
1409 IOObjectRelease( iterator );
1410
1411 iterator = 0;
1412 while ( service && !IOObjectConformsTo( service, "IOCDMedia" ))
1413 {
1414 char buffer[1024];
1415 IOObjectGetClass(service,buffer);
1416 printf("Service: %s\n",buffer);
1417
1418 if ( IORegistryEntryGetParentIterator( service, kIOServicePlane, &iterator ) != KERN_SUCCESS )
1419 {
1420 printf( "IORegistryEntryGetParentIterator failed\n" );
1421 goto Exit ;
1422 }
1423
1424 IOObjectRelease( service );
1425 service = IOIteratorNext( iterator );
1426 IOObjectRelease( iterator );
1427
1428 }
1429 if ( service == NULL )
1430 {
1431 printf( "CD media not found\n" ); goto Exit ;
1432 }
1433 if ( IORegistryEntryCreateCFProperties( service, (__CFDictionary **) &properties,
1434 kCFAllocatorDefault,
1435 kNilOptions ) != KERN_SUCCESS )
1436 {
1437 printf( "IORegistryEntryGetParentIterator failed\n" ); goto Exit ;
1438 }
1439
1440 data = (CFDataRef) CFDictionaryGetValue( properties, CFSTR( "TOC" ) );
1441 if ( data == NULL )
1442 {
1443 printf( "CFDictionaryGetValue failed\n" ); goto Exit ;
1444 }
1445 else
1446 {
1447
1448 CFRange range;
1449 CFIndex buflen;
1450
1451 buflen = CFDataGetLength( data ) + 1;
1452 range = CFRangeMake( 0, buflen );
1453 toc_p = ( struct _CDTOC *) malloc( buflen );
1454 if ( toc_p == NULL )
1455 {
1456 printf( "Out of memory\n" ); goto Exit ;
1457 }
1458 else
1459 {
1460 CFDataGetBytes( data, range, ( unsigned char *) toc_p );
1461 }
1462
1463 /*
1464 fprintf( stderr, "Table of contents\n length %d first %d last %d\n",
1465 toc_p->length, toc_p->first_session, toc_p->last_session );
1466 */
1467
1468 CFRelease( properties );
1469
1470 }
1471 Exit :
1472 if ( service )
1473 {
1474 IOObjectRelease( service );
1475 }
1476
1477 return toc_p;
1478 }
1479 #endif // _CARBON_
1480
1481 DiskImage::DiskImage(int disk)
1482 {
1483 Init();
1484 #ifdef _WIN32_
1485 char buffer[1024];
1486 sprintf(buffer,"%c:\\",'A'+disk);
1487 mSize = GetFileSize(buffer,NULL);
1488
1489 sprintf(buffer,"\\\\.\\%c:",'a'+disk);
1490 mFile = CreateFile(buffer, GENERIC_READ,0,NULL,OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS,NULL);
1491
1492 DWORD junk;
1493 DISK_GEOMETRY dg;
1494 DISK_GEOMETRY* pdg = &dg;
1495 BOOL res = DeviceIoControl(mFile,
1496 IOCTL_DISK_GET_DRIVE_GEOMETRY,
1497 NULL, 0,
1498 pdg, sizeof(*pdg),
1499 &junk,
1500 NULL);
1501
1502 if (res)
1503 {
1504 mSize = dg.BytesPerSector * dg.SectorsPerTrack * dg.TracksPerCylinder * dg.Cylinders.LowPart;
1505 mClusterSize = dg.BytesPerSector;
1506 }
1507 #elif defined(_CARBON_) || defined(__APPLE__)
1508 kern_return_t kernResult;
1509 io_iterator_t mediaIterator;
1510 char bsdPath[ MAXPATHLEN ];
1511 kernResult = FindEjectableCDMedia( &mediaIterator );
1512 kernResult = GetBSDPath( mediaIterator, bsdPath, sizeof( bsdPath ) );
1513 if ( bsdPath[ 0 ] != '\0' )
1514 {
1515 strcpy(bsdPath,"/dev/rdisk1s0");
1516
1517 struct _CDTOC * toc = ReadTOC( bsdPath );
1518 if ( toc )
1519 {
1520 size_t toc_entries = ( toc->length - 2 ) / sizeof (struct _CDTOC_Desc );
1521
1522 int start_sector = -1;
1523 int data_track = -1;
1524 // Iterate through the list backward. Pick the first data track and
1525 // get the address of the immediately previous (or following depending
1526 // on how you look at it). The difference in the sector numbers
1527 // is returned as the sized of the data track.
1528 for (ssize_t i=toc_entries - 1; i>=0; i-- )
1529 {
1530 if ( start_sector != -1 )
1531 {
1532 start_sector = MSF_TO_LBA(toc->trackdesc[i].p) - start_sector;
1533 break ;
1534
1535 }
1536 if (( toc->trackdesc[i].ctrl_adr >> 4) != 1 )
1537 continue ;
1538 if ( toc->trackdesc[i].ctrl_adr & 0x04 )
1539 {
1540 data_track = toc->trackdesc[i].point;
1541 start_sector = MSF_TO_LBA(toc->trackdesc[i].p);
1542 }
1543 }
1544
1545 free( toc );
1546 if ( start_sector == -1 )
1547 {
1548 start_sector = 0;
1549 }
1550 }
1551 // mSize = start_sector;
1552 // printf("size %d\n",mSize);
1553
1554
1555 mFile = open(bsdPath,O_RDONLY);
1556 if (!mFile)
1557 printf("Error while opening file: %s\n",bsdPath);
1558 else
1559 {
1560 printf("opened file: %s\n",bsdPath);
1561
1562 mSize = (int) lseek(mFile,1000000000,SEEK_SET);
1563 printf("size %d\n",mSize);
1564 lseek(mFile,0,SEEK_SET);
1565
1566 mSize = 700 * 1024 * 1024;
1567
1568 }
1569 }
1570 if ( mediaIterator )
1571 {
1572 IOObjectRelease( mediaIterator );
1573 }
1574 #elif LINUX
1575 OpenStream("/dev/cdrom");
1576 #endif
1577 }
1578
1579 void DiskImage::Init()
1580 {
1581 mFile = 0;
1582 mPos = 0;
1583 mCluster = (uint)-1;
1584 mStartFrame = -1;
1585 mEndFrame = -1;
1586 #ifdef WIN32
1587 mpCache = (char*) VirtualAlloc(NULL,mClusterSize,MEM_COMMIT,PAGE_READWRITE);
1588 #else
1589 mpCache = NULL; // we allocate the cache later when we know what type of media we access
1590 #endif
1591 }
1592
1593 DiskImage::~DiskImage()
1594 {
1595 #ifdef WIN32
1596 if (mFile != INVALID_HANDLE_VALUE)
1597 {
1598 CloseHandle(mFile);
1599 }
1600 #elif defined _CARBON_ || defined(__APPLE__) || LINUX
1601 if (mFile)
1602 {
1603 close(mFile);
1604 }
1605 #endif
1606 if (mpCache)
1607 {
1608 #ifdef WIN32
1609 VirtualFree(mpCache, 0, MEM_RELEASE);
1610 #elif defined(_CARBON_) || defined(__APPLE__) || LINUX
1611 free(mpCache);
1612 #endif
1613 }
1614 }
1615
1616 akai_stream_state_t DiskImage::GetState() const
1617 {
1618 if (!mFile) return akai_stream_closed;
1619 if (mPos > mSize) return akai_stream_end_reached;
1620 return akai_stream_ready;
1621 }
1622
1623 int DiskImage::GetPos() const
1624 {
1625 return mPos;
1626 }
1627
1628 int DiskImage::SetPos(int Where, akai_stream_whence_t Whence)
1629 {
1630 // printf("setpos %d\n",Where);
1631 int w = Where;
1632 switch (Whence)
1633 {
1634 case akai_stream_start:
1635 mPos = w;
1636 break;
1637 /*case eStreamRewind:
1638 w = -w;*/
1639 case akai_stream_curpos:
1640 mPos += w;
1641 break;
1642 case akai_stream_end:
1643 mPos = mSize - w;
1644 break;
1645 }
1646 // if (mPos > mSize)
1647 // mPos = mSize;
1648 if (mPos < 0)
1649 mPos = 0;
1650 return mPos;
1651 }
1652
1653 int DiskImage::Available (uint WordSize)
1654 {
1655 return (mSize-mPos)/WordSize;
1656 }
1657
1658 int DiskImage::Read(void* pData, uint WordCount, uint WordSize)
1659 {
1660 int readbytes = 0;
1661 int sizetoread = WordCount * WordSize;
1662
1663 while (sizetoread > 0) {
1664 if (mSize <= mPos) return readbytes / WordSize;
1665 int requestedCluster = (mRegularFile) ? mPos / mClusterSize
1666 : mPos / mClusterSize + mStartFrame;
1667 if (mCluster != requestedCluster) { // read the requested cluster into cache
1668 mCluster = requestedCluster;
1669 #ifdef WIN32
1670 if (mCluster * mClusterSize != SetFilePointer(mFile, mCluster * mClusterSize, NULL, FILE_BEGIN)) {
1671 printf("ERROR: couldn't seek device!\n");
1672 #if 0 // FIXME: endian correction is missing correct detection
1673 if ((readbytes > 0) && (mEndian != eEndianNative)) {
1674 switch (WordSize) {
1675 case 2: bswap_16_s ((uint16*)pData, readbytes); break;
1676 case 4: bswap_32_s ((uint32*)pData, readbytes); break;
1677 case 8: bswap_64_s ((uint64*)pData, readbytes); break;
1678 }
1679 }
1680 #endif
1681 return readbytes / WordSize;
1682 }
1683 DWORD size;
1684 ReadFile(mFile, mpCache, mClusterSize, &size, NULL);
1685 #elif defined(_CARBON_) || defined(__APPLE__) || LINUX
1686 if (mCluster * mClusterSize != lseek(mFile, mCluster * mClusterSize, SEEK_SET))
1687 return readbytes / WordSize;
1688 // printf("trying to read %d bytes from device!\n",mClusterSize);
1689 /*int size =*/ read(mFile, mpCache, mClusterSize);
1690 // printf("read %d bytes from device!\n",size);
1691 #endif
1692 }
1693 // printf("read %d bytes at pos %d\n",WordCount*WordSize,mPos);
1694
1695 int currentReadSize = sizetoread;
1696 int posInCluster = mPos % mClusterSize;
1697 if (currentReadSize > mClusterSize - posInCluster) // restrict to this current cached cluster.
1698 currentReadSize = mClusterSize - posInCluster;
1699
1700 memcpy((uint8_t*)pData + readbytes, mpCache + posInCluster, currentReadSize);
1701
1702 mPos += currentReadSize;
1703 readbytes += currentReadSize;
1704 sizetoread -= currentReadSize;
1705 // printf("new pos %d\n",mPos);
1706 }
1707
1708 #if 0 // FIXME: endian correction is missing correct detection
1709 if ((readbytes > 0) && (mEndian != eEndianNative))
1710 switch (WordSize)
1711 {
1712 case 2: bswap_16_s ((uint16_t*)pData, readbytes); break;
1713 case 4: bswap_32_s ((uint32_t*)pData, readbytes); break;
1714 case 8: bswap_64_s ((uint64_t*)pData, readbytes); break;
1715 }
1716 #endif
1717
1718 return readbytes / WordSize;
1719 }
1720
1721 void DiskImage::ReadInt8(uint8_t* pData, uint WordCount) {
1722 Read(pData, WordCount, 1);
1723 }
1724
1725 void DiskImage::ReadInt16(uint16_t* pData, uint WordCount) {
1726 int i;
1727 for (i = 0; i < WordCount; i++) {
1728 *(pData + i) = ReadInt16();
1729 }
1730 }
1731
1732 void DiskImage::ReadInt32(uint32_t* pData, uint WordCount) {
1733 int i;
1734 for (i = 0; i < WordCount; i++) {
1735 *(pData + i) = ReadInt32();
1736 }
1737 }
1738
1739 int DiskImage::ReadInt8(uint8_t* pData) {
1740 return Read(pData, 1, 1);
1741 }
1742
1743 int DiskImage::ReadInt16(uint16_t* pData) {
1744 int result = Read(pData, 1, 2);
1745 #if WORDS_BIGENDIAN
1746 swapBytes_16(pData);
1747 #endif
1748 return result;
1749 }
1750
1751 int DiskImage::ReadInt32(uint32_t* pData) {
1752 int result = Read(pData, 1, 4);
1753 #if WORDS_BIGENDIAN
1754 swapBytes_32(pData);
1755 #endif
1756 return result;
1757 }
1758
1759 uint8_t DiskImage::ReadInt8()
1760 {
1761 uint8_t word;
1762 Read(&word,1,1);
1763 return word;
1764 }
1765
1766 uint16_t DiskImage::ReadInt16()
1767 {
1768 uint16_t word;
1769 Read(&word,1,2);
1770 #if WORDS_BIGENDIAN
1771 swapBytes_16(&word);
1772 #endif
1773 return word;
1774 }
1775
1776 uint32_t DiskImage::ReadInt32()
1777 {
1778 uint32_t word;
1779 Read(&word,1,4);
1780 #if WORDS_BIGENDIAN
1781 swapBytes_32(&word);
1782 #endif
1783 return word;
1784 }
1785
1786 void DiskImage::OpenStream(const char* path) {
1787 #ifdef WIN32
1788 mFile = CreateFile(path, GENERIC_READ,0,NULL,OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS,NULL);
1789 BY_HANDLE_FILE_INFORMATION FileInfo;
1790 GetFileInformationByHandle(mFile,&FileInfo);
1791 mSize = FileInfo.nFileSizeLow;
1792 #elif defined(_CARBON_) || defined(__APPLE__) || LINUX
1793 struct stat filestat;
1794 stat(path,&filestat);
1795 mFile = open(path, O_RDONLY | O_NONBLOCK);
1796 if (mFile <= 0) {
1797 printf("Can't open %s\n", path);
1798 mFile = 0;
1799 return;
1800 }
1801 // TODO: we should also check here if 'path' is a link or something
1802 if (S_ISREG(filestat.st_mode)) { // regular file
1803 printf("Using regular Akai image file.\n");
1804 mRegularFile = true;
1805 mSize = (int) filestat.st_size;
1806 mClusterSize = DISK_CLUSTER_SIZE;
1807 mpCache = (char*) malloc(mClusterSize);
1808 } else { // CDROM
1809 #if defined(_CARBON_) || defined(__APPLE__)
1810 printf("Can't open %s: not a regular file\n", path);
1811 #else // Linux ...
1812 mRegularFile = false;
1813 mClusterSize = CD_FRAMESIZE;
1814 mpCache = (char*) malloc(mClusterSize);
1815
1816 struct cdrom_tochdr tochdr;
1817 struct cdrom_tocentry tocentry;
1818 int prev_addr = 0;
1819 if (ioctl(mFile, CDROMREADTOCHDR, (unsigned long)&tochdr) < 0) {
1820 printf("Trying to read TOC of %s failed\n", path);
1821 close(mFile);
1822 mFile = 0;
1823 return;
1824 }
1825 printf("Total tracks: %d\n", tochdr.cdth_trk1);
1826
1827 /* we access the CD with logical blocks as entity */
1828 tocentry.cdte_format = CDROM_LBA;
1829
1830 int firstDataTrack = -1;
1831 int start, end, length;
1832
1833 /* we pick up the lowest data track by iterating backwards, starting with Lead Out */
1834 for (int t = tochdr.cdth_trk1; t >= 0; t--) {
1835 tocentry.cdte_track = (t == tochdr.cdth_trk1) ? CDROM_LEADOUT : t + 1;
1836 if (ioctl(mFile, CDROMREADTOCENTRY, (unsigned long)&tocentry) < 0){
1837 printf("Failed to read TOC entry for track %d\n", tocentry.cdte_track);
1838 close(mFile);
1839 mFile = 0;
1840 return;
1841 }
1842 if (tocentry.cdte_track == CDROM_LEADOUT) {
1843 printf("Lead Out: Start(LBA)=%d\n", tocentry.cdte_addr.lba);
1844 }
1845 else {
1846 printf("Track %d: Start(LBA)=%d End(LBA)=%d Length(Blocks)=%d ",
1847 tocentry.cdte_track, tocentry.cdte_addr.lba, prev_addr - 1, prev_addr - tocentry.cdte_addr.lba);
1848 if (tocentry.cdte_ctrl & CDROM_DATA_TRACK) {
1849 printf("Type: Data\n");
1850 firstDataTrack = tocentry.cdte_track;
1851 start = tocentry.cdte_addr.lba;
1852 end = prev_addr - 1;
1853 length = prev_addr - tocentry.cdte_addr.lba;
1854 }
1855 else printf("Type: Audio\n");
1856 }
1857 prev_addr = tocentry.cdte_addr.lba;
1858 }
1859
1860 if (firstDataTrack == -1) {
1861 printf("Sorry, no data track found on %s\n", path);
1862 close(mFile);
1863 mFile = 0;
1864 return;
1865 }
1866
1867 printf("Ok, I'll pick track %d\n", firstDataTrack);
1868 mStartFrame = start;
1869 mEndFrame = end;
1870 mSize = length * CD_FRAMESIZE;
1871 #endif
1872 } // CDROM
1873 #endif
1874 }
1875
1876 bool DiskImage::WriteImage(const char* path)
1877 {
1878 #if defined(_CARBON_) || defined(__APPLE__) || LINUX
1879 const uint bufferSize = 524288; // 512 kB
1880 int fOut = open(path, O_WRONLY | O_NONBLOCK | O_CREAT | O_TRUNC,
1881 S_IRUSR | S_IWUSR | S_IRGRP);
1882 if (mFile <= 0) {
1883 printf("Can't open output file %s\n", path);
1884 return false;
1885 }
1886 uint8_t* pBuffer = new uint8_t[bufferSize];
1887 SetPos(0);
1888 while (Available() > 0) {
1889 int readBytes = Read(pBuffer,bufferSize,1);
1890 if (readBytes > 0) write(fOut,pBuffer,readBytes);
1891 }
1892 delete[] pBuffer;
1893 close(fOut);
1894 return true;
1895 #endif // _CARBON_ || LINUX
1896 return false;
1897 }
1898
1899 inline void DiskImage::swapBytes_16(void* Word) {
1900 uint8_t byteCache;
1901 byteCache = *((uint8_t*) Word);
1902 *((uint8_t*) Word) = *((uint8_t*) Word + 1);
1903 *((uint8_t*) Word + 1) = byteCache;
1904 }
1905
1906 inline void DiskImage::swapBytes_32(void* Word) {
1907 uint8_t byteCache;
1908 byteCache = *((uint8_t*) Word);
1909 *((uint8_t*) Word) = *((uint8_t*) Word + 3);
1910 *((uint8_t*) Word + 3) = byteCache;
1911 byteCache = *((uint8_t*) Word + 1);
1912 *((uint8_t*) Word + 1) = *((uint8_t*) Word + 2);
1913 *((uint8_t*) Word + 2) = byteCache;
1914 }

Properties

Name Value
svn:executable *

  ViewVC Help
Powered by ViewVC